1 /* ------------------------------------------------------------------ 2 * Copyright (C) 1998-2009 PacketVideo 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 * express or implied. 14 * See the License for the specific language governing permissions 15 * and limitations under the License. 16 * ------------------------------------------------------------------- 17 */ 18 #include "pvmf_protocol_engine_progressive_download.h" 19 20 ////// ProgressiveDownloadState_HEAD implementation //////////////////////////// 21 OSCL_EXPORT_REF void ProgressiveDownloadState_HEAD::setRequestBasics() 22 { 23 iComposer->setMethod(HTTP_METHOD_HEAD); 24 //iComposer->setVersion(HTTP_V1_1); 25 iComposer->setVersion((HTTPVersion)iCfgFile->getHttpVersion()); 26 StrPtrLen uri((iURI.getURI()).get_cstr(), (iURI.getURI()).get_size()); 27 iComposer->setURI(uri); 28 } 29 30 OSCL_EXPORT_REF bool ProgressiveDownloadState_HEAD::setHeaderFields() 31 { 32 if (!DownloadState::setHeaderFields()) return false; 33 if (!ProtocolState::constructAuthenHeader(iCfgFile->GetUserId(), iCfgFile->GetUserAuth())) return false; 34 35 return setExtensionFields(iCfgFile->getExtensionHeaderKeys(), 36 iCfgFile->getExtensionHeaderValues(), 37 iCfgFile->getHTTPMethodMasksForExtensionHeader(), 38 iCfgFile->getExtensionHeadersPurgeOnRedirect(), 39 HTTP_METHOD_HEAD); 40 } 41 42 OSCL_EXPORT_REF int32 ProgressiveDownloadState_HEAD::checkParsingStatus(int32 parsingStatus) 43 { 44 if (parsingStatus == HttpParsingBasicObject::PARSE_SUCCESS_END_OF_INPUT && iParser->isHttpHeaderParsed()) 45 return PROCESS_SUCCESS_END_OF_MESSAGE; 46 47 return ProtocolState::checkParsingStatus(parsingStatus); 48 } 49 50 OSCL_EXPORT_REF int32 ProgressiveDownloadState_HEAD::OutputDataAvailable(OUTPUT_DATA_QUEUE *aOutputQueue, const bool isHttpHeader) 51 { 52 if (isHttpHeader) 53 { 54 iDataSideInfo.set(ProtocolEngineOutputDataType_HttpHeader); 55 iObserver->OutputDataAvailable(*aOutputQueue, iDataSideInfo); 56 } 57 return HttpParsingBasicObject::PARSE_SUCCESS; 58 } 59 60 ////// ProgressiveDownloadState_GET implementation //////////////////////////// 61 OSCL_EXPORT_REF int32 ProgressiveDownloadState_GET::processMicroStateGetResponsePreCheck() 62 { 63 int32 status = DownloadState::processMicroStateGetResponsePreCheck(); 64 if (status != PROCESS_SUCCESS) return status; 65 66 // set the existing download size if this is resume download 67 iParser->setDownloadSize(iCfgFile->GetCurrentFileSize()); 68 69 return PROCESS_SUCCESS; 70 } 71 72 OSCL_EXPORT_REF bool ProgressiveDownloadState_GET::setHeaderFields() 73 { 74 // check range header 75 if (!setRangeHeaderFields()) return false; 76 77 // set authentication header and common headers 78 if (!ProtocolState::constructAuthenHeader(iCfgFile->GetUserId(), iCfgFile->GetUserAuth())) return false; 79 if (!DownloadState::setHeaderFields()) return false; 80 81 // change "Connection" field 82 StrCSumPtrLen connectionKey = "Connection"; 83 char *nullPtr = NULL; // remove "Connection" field 84 if (!iComposer->setField(connectionKey, nullPtr)) return false; 85 // reset "Connection: Close" 86 StrPtrLen connectionValue = "Close"; 87 if (!iComposer->setField(connectionKey, &connectionValue)) return false; 88 89 90 return setExtensionFields(iCfgFile->getExtensionHeaderKeys(), 91 iCfgFile->getExtensionHeaderValues(), 92 iCfgFile->getHTTPMethodMasksForExtensionHeader(), 93 iCfgFile->getExtensionHeadersPurgeOnRedirect()); 94 } 95 96 OSCL_EXPORT_REF bool ProgressiveDownloadState_GET::setRangeHeaderFields() 97 { 98 if (iRangeHeaderSupported) 99 { 100 // only send Range header for previous non-zero bytes position. 101 // Some server may not like this, Range: bytes=0- 102 if (iCfgFile->GetCurrentFileSize() > 0 && iCfgFile->GetOverallFileSize() > 0) 103 { 104 StrCSumPtrLen rangeKey = "Range"; 105 char buffer[64]; 106 oscl_snprintf(buffer, 64, "bytes=%d-%d", iCfgFile->GetCurrentFileSize(), iCfgFile->GetOverallFileSize()); 107 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::setHeaderFields(), Range: bytes=%d-", iCfgFile->GetCurrentFileSize())); 108 if (!iComposer->setField(rangeKey, buffer)) return false; 109 } 110 } 111 return true; 112 } 113 114 OSCL_EXPORT_REF int32 ProgressiveDownloadState_GET::updateDownloadStatistics() 115 { 116 int32 status = DownloadState::updateDownloadStatistics(); 117 if (status == PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED) 118 { 119 iSendEndOfMessageTruncate = true; 120 } 121 return status; 122 } 123 124 125 OSCL_EXPORT_REF int32 ProgressiveDownloadState_GET::checkParsingStatus(int32 parsingStatus) 126 { 127 // EOS means connection is down, and can be treated as download complete 128 if (parsingStatus == HttpParsingBasicObject::PARSE_EOS_INPUT_DATA) 129 { 130 if (iParser->getDownloadSize() > 0 && iParser->isDownloadReallyHappen()) 131 { 132 iCfgFile->SetCurrentFileSize(iParser->getDownloadSize()); 133 if (iParser->getContentLength() == 0) iCfgFile->SetOverallFileSize(iParser->getDownloadSize()); 134 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::checkParsingStatus(), GOT EOS and COMPLETE DOWNLOAD, downloadSize=%d, contentLength=%d, isDownloadHappen=%d", 135 iParser->getDownloadSize(), iParser->getContentLength(), (int32)iParser->isDownloadReallyHappen())); 136 return PROCESS_SUCCESS_END_OF_MESSAGE_BY_SERVER_DISCONNECT; 137 } 138 } 139 140 // download complete with truncation 141 if (iSendEndOfMessageTruncate) 142 { 143 iSendEndOfMessageTruncate = false; 144 return PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED; 145 } 146 147 return DownloadState::checkParsingStatus(parsingStatus); 148 } 149 150 151 // From HttpParsingBasicObjectObserver 152 OSCL_EXPORT_REF int32 ProgressiveDownloadState_GET::OutputDataAvailable(OUTPUT_DATA_QUEUE *aOutputQueue, const bool isHttpHeader) 153 { 154 if (isHttpHeader) 155 { 156 int32 status = checkContentInfoMatchingForResumeDownload(); 157 if (status != HttpParsingBasicObject::PARSE_SUCCESS) return status; 158 159 iDataSideInfo.set(ProtocolEngineOutputDataType_HttpHeader); 160 iObserver->OutputDataAvailable(*aOutputQueue, iDataSideInfo); 161 } 162 else // output data to data stream object 163 { 164 if (iParser->getDownloadSize() > iCfgFile->GetCurrentFileSize()) 165 { 166 updateOutputDataQueue(aOutputQueue); // aOutputQueue could have the partial valid data for resume download and trucated content case 167 iDataSideInfo.set(ProtocolEngineOutputDataType_NormalData); 168 iObserver->OutputDataAvailable(*aOutputQueue, iDataSideInfo); 169 return updateDownloadStatistics(); // could return PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED 170 } 171 } 172 return HttpParsingBasicObject::PARSE_SUCCESS; 173 } 174 175 OSCL_EXPORT_REF int32 ProgressiveDownloadState_GET::checkContentInfoMatchingForResumeDownload() 176 { 177 if (iCfgFile->IsNewSession()) return HttpParsingBasicObject::PARSE_SUCCESS; 178 uint32 prevOverallFileSize = iCfgFile->GetOverallFileSize(); 179 if (iCfgFile->GetOverallFileSize() == iCfgFile->GetMaxAllowedFileSize() && !iCfgFile->HasContentLength()) 180 { 181 prevOverallFileSize = 0; // no content-length for the previous download 182 } 183 int32 status = iParser->isNewContentRangeInfoMatchingCurrentOne(prevOverallFileSize); 184 // Get internal download size synced up with new content-range info 185 iParser->setDownloadSize(); 186 return status; 187 } 188 189 OSCL_EXPORT_REF void ProgressiveDownloadState_GET::updateOutputDataQueue(OUTPUT_DATA_QUEUE *aOutputQueue) 190 { 191 // get start fragment especially for resume download case 192 bool aUseAllNewDownloadData; 193 uint32 aStartFragNo = 0, aStartFragOffset = 0; 194 getStartFragmentInNewDownloadData(*aOutputQueue, aUseAllNewDownloadData, aStartFragNo, aStartFragOffset); 195 if (aUseAllNewDownloadData) return; 196 197 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getStartFragmentInNewDownloadData() : aOutputQueue->size=%d, aStartFragNo=%d, aStartFragOffset=%d", 198 aOutputQueue->size(), aStartFragNo, aStartFragOffset)); 199 200 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getStartFragmentInNewDownloadData() : downloadSize=%d, currFileSize=%d", 201 iParser->getDownloadSize(), iCfgFile->GetCurrentFileSize())); 202 203 // process start fragment 204 if (!(aStartFragNo == 0 && aStartFragOffset == 0)) // exist offset 205 { 206 OsclMemoryFragment memFrag; 207 uint8 *startPtr = (uint8*)((*aOutputQueue)[aStartFragNo].getMemFragPtr()) + aStartFragOffset; 208 memFrag.ptr = (OsclAny*)startPtr; 209 memFrag.len = (*aOutputQueue)[aStartFragNo].getMemFragSize() - aStartFragOffset; 210 OsclRefCounter *refcnt = (*aOutputQueue)[aStartFragNo].getRefCounter(); 211 OsclRefCounterMemFrag refCountMemFrag = OsclRefCounterMemFrag(memFrag, refcnt, memFrag.len); 212 refcnt->addRef(); // manually add reference counter since there will be vector push_back happens. 213 214 for (uint32 i = 0; i <= aStartFragNo; i++) 215 { 216 aOutputQueue->erase(aOutputQueue->begin()); 217 } 218 if (memFrag.len > 0) aOutputQueue->push_front(refCountMemFrag); 219 220 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue() after processing start fragment: aOutputQueue->size=%d", aOutputQueue->size())); 221 } 222 223 // get end fragment especially for truncated content case 224 uint32 aEndFragNo = 0, aEndFragValidLen = 0; 225 getEndFragmentInNewDownloadData(*aOutputQueue, aEndFragNo, aEndFragValidLen); 226 227 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getEndFragmentInNewDownloadData() : aOutputQueue->size=%d, aEndFragNo=%d, aEndFragValidLen=%d", 228 aOutputQueue->size(), aEndFragNo, aEndFragValidLen)); 229 230 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getStartFragmentInNewDownloadData() : downloadSize=%d, overallFileSize=%d", 231 iParser->getDownloadSize(), iCfgFile->GetOverallFileSize())); 232 233 // process end fragment 234 if (!(aEndFragNo == aOutputQueue->size() - 1 && 235 aEndFragValidLen == (*aOutputQueue)[aEndFragNo].getMemFragSize())) 236 { 237 OsclMemoryFragment memFrag; 238 memFrag.ptr = (*aOutputQueue)[aEndFragNo].getMemFragPtr(); 239 memFrag.len = aEndFragValidLen; 240 OsclRefCounter *refcnt = (*aOutputQueue)[aEndFragNo].getRefCounter(); 241 OsclRefCounterMemFrag refCountMemFrag = OsclRefCounterMemFrag(memFrag, refcnt, memFrag.len); 242 refcnt->addRef(); // manually add reference counter since there will be vector push_back happens. 243 244 for (int32 j = (int32)aOutputQueue->size() - 1; j >= (int32)aEndFragNo; j--) 245 { 246 aOutputQueue->erase(&(aOutputQueue->back())); 247 } 248 249 aOutputQueue->push_back(refCountMemFrag); 250 251 LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue() after processing end fragment: aOutputQueue->size=%d", aOutputQueue->size())); 252 } 253 } 254 255 OSCL_EXPORT_REF void ProgressiveDownloadState_GET::getStartFragmentInNewDownloadData(OUTPUT_DATA_QUEUE &aOutputQueue, 256 bool &aUseAllNewDownloadData, 257 uint32 &aStartFragNo, 258 uint32 &aStartFragOffset) 259 { 260 aUseAllNewDownloadData = false; 261 aStartFragNo = aStartFragOffset = 0; 262 263 uint32 validSize = iParser->getDownloadSize() - iCfgFile->GetCurrentFileSize(); 264 265 uint32 totalSize = 0, prevTotalSize = 0; 266 for (uint32 i = 0; i < aOutputQueue.size(); i++) 267 { 268 prevTotalSize = totalSize; 269 totalSize += aOutputQueue[i].getMemFragSize(); 270 if (prevTotalSize <= validSize && validSize < totalSize) 271 { 272 if (validSize < totalSize && i < aOutputQueue.size() - 1) 273 { 274 aStartFragNo = i; 275 aStartFragOffset = validSize - prevTotalSize; 276 return; 277 } 278 } 279 } 280 281 aUseAllNewDownloadData = (validSize == totalSize) & 282 (iParser->getDownloadSize() <= iCfgFile->GetOverallFileSize()); 283 } 284 285 OSCL_EXPORT_REF void ProgressiveDownloadState_GET::getEndFragmentInNewDownloadData(OUTPUT_DATA_QUEUE &aOutputQueue, 286 uint32 &aEndFragNo, 287 uint32 &aEndFragValidLen) 288 { 289 aEndFragNo = aOutputQueue.size() - 1; 290 aEndFragValidLen = aOutputQueue[aEndFragNo].getMemFragSize(); 291 292 if (iParser->getDownloadSize() > iCfgFile->GetOverallFileSize()) 293 { 294 uint32 extraSize = iParser->getDownloadSize() - iCfgFile->GetOverallFileSize(); 295 uint32 reduceSize = 0, prevReduceSize = 0; 296 for (int32 i = aOutputQueue.size() - 1; i >= 0; i--) 297 { 298 prevReduceSize = reduceSize; 299 reduceSize += aOutputQueue[i].getMemFragSize(); 300 if (prevReduceSize <= extraSize && extraSize < reduceSize) 301 { 302 aEndFragNo = i; 303 aEndFragValidLen = reduceSize - extraSize; 304 return; 305 } 306 } 307 } 308 } 309 310 //////////////////////////////////////////////////////////////////////////////////// 311 ////// ProgressiveStreamingState implementation 312 //////////////////////////////////////////////////////////////////////////////////// 313 314 OSCL_EXPORT_REF int32 ProgressiveStreamingState_GET::checkParsingStatus(int32 parsingStatus) 315 { 316 // download complete with truncation 317 if (iSendEndOfMessageTruncate) 318 { 319 iSendEndOfMessageTruncate = false; 320 return PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED; 321 } 322 323 return DownloadState::checkParsingStatus(parsingStatus); 324 } 325 326