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 ////////////////////////////
setRequestBasics()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
setHeaderFields()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
checkParsingStatus(int32 parsingStatus)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
OutputDataAvailable(OUTPUT_DATA_QUEUE * aOutputQueue,const bool isHttpHeader)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 ////////////////////////////
processMicroStateGetResponsePreCheck()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
setHeaderFields()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
setRangeHeaderFields()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
updateDownloadStatistics()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
checkParsingStatus(int32 parsingStatus)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
OutputDataAvailable(OUTPUT_DATA_QUEUE * aOutputQueue,const bool isHttpHeader)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
checkContentInfoMatchingForResumeDownload()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
updateOutputDataQueue(OUTPUT_DATA_QUEUE * aOutputQueue)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
getStartFragmentInNewDownloadData(OUTPUT_DATA_QUEUE & aOutputQueue,bool & aUseAllNewDownloadData,uint32 & aStartFragNo,uint32 & aStartFragOffset)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
getEndFragmentInNewDownloadData(OUTPUT_DATA_QUEUE & aOutputQueue,uint32 & aEndFragNo,uint32 & aEndFragValidLen)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
checkParsingStatus(int32 parsingStatus)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