|
1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "chttpmessageparser.h" |
|
17 |
|
18 #include <inetprottextutils.h> |
|
19 #include <uriutilscommon.h> |
|
20 |
|
21 #include "mhttpmessageparserobserver.h" |
|
22 #include "thttpmessagepanic.h" |
|
23 |
|
24 const TInt KDefaultBufferSize = 128; |
|
25 const TUint KColon = ':'; |
|
26 _LIT8 ( KLineFeed, "\n" ); |
|
27 |
|
28 // 'this' used in base member initializer list, The 'this' pointer being used is a base class pointer. |
|
29 #pragma warning( disable : 4355 ) |
|
30 |
|
31 CHttpMessageParser* CHttpMessageParser::NewL(MHttpMessageParserObserver& aObserver) |
|
32 /** |
|
33 Factory constructor. |
|
34 @param aObserver The observer for the parser. |
|
35 @return A pointer to a fully constructed and initialised object. |
|
36 */ |
|
37 { |
|
38 return new (ELeave) CHttpMessageParser(aObserver); |
|
39 } |
|
40 |
|
41 CHttpMessageParser::~CHttpMessageParser() |
|
42 /** |
|
43 Destructor |
|
44 */ |
|
45 { |
|
46 Cancel(); |
|
47 |
|
48 // Cleanup |
|
49 delete iLineBuffer; |
|
50 } |
|
51 |
|
52 CHttpMessageParser::CHttpMessageParser(MHttpMessageParserObserver& aObserver) |
|
53 : CActive(CActive::EPriorityStandard + 1), iObserver(aObserver), iDataParser(*this) |
|
54 /** |
|
55 Constructor. |
|
56 See note in CHttpMessageComposer |
|
57 */ |
|
58 { |
|
59 CActiveScheduler::Add(this); |
|
60 } |
|
61 |
|
62 void CHttpMessageParser::ReceivedMessageData() |
|
63 /** |
|
64 Notifies the parser of more message data. The parser gets the data packet |
|
65 from the observer and continues processing its state machine. |
|
66 @panic EHttpMessagePanicBadDataState The current data packet has not been |
|
67 completely parsed. |
|
68 */ |
|
69 { |
|
70 __ASSERT_DEBUG( iDataState == EWaitingForData, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) ); |
|
71 |
|
72 // Have now got some data - update the data state |
|
73 iDataState = EGotData; |
|
74 |
|
75 // Get the received data packet |
|
76 TPtrC8 data; |
|
77 iObserver.GetDataPacket(data); |
|
78 |
|
79 // Pass it to the data parser |
|
80 iDataParser.SetData(data); |
|
81 |
|
82 // Continue parsing... |
|
83 CompleteSelf(); |
|
84 } |
|
85 |
|
86 void CHttpMessageParser::CompletedBodyDataL() |
|
87 /** |
|
88 Tells the parser that there is no more body data. When using HTTP 1.0 style |
|
89 responses the body data maybe of unknown length so this method allows clients |
|
90 to inform the parser that the body data is completed. |
|
91 */ |
|
92 { |
|
93 iDataSizeLeft = 0; |
|
94 |
|
95 // Notify the observer that the body has been received |
|
96 iObserver.BodyCompleteL(); |
|
97 |
|
98 // Notify the observer that the message is complete. |
|
99 iObserver.MessageCompleteL(KNullDesC8()); |
|
100 |
|
101 // Move to the Idle state. |
|
102 iParserState = EIdle; |
|
103 iDataState = EWaitingForData; |
|
104 } |
|
105 |
|
106 void CHttpMessageParser::Reset() |
|
107 /** |
|
108 Parser reset request. As the observer can reset the parser during one the |
|
109 callback functions, the parser must check for re-entrancy to avoid releasing |
|
110 resources that are still required. If the parser is either waiting for more |
|
111 message data or is waiting to process its state machine, the parser can |
|
112 safely reset immediately. Otherwise the parser is being reset from within |
|
113 its RunL() and so it must defer resetting itself to a safer point. This is |
|
114 the point in the RunL() where the next step is decided. |
|
115 @panic EHttpMessagePanicDoubleReset The parser has been reset twice in one |
|
116 of the observer callback functions. |
|
117 */ |
|
118 { |
|
119 // Check the data state of the parser - the parser cannot be reset if the |
|
120 // Reset() was called in one of the observer callbacks. It is safe to reset |
|
121 // now if - |
|
122 // 1) the data state is WaitingForData |
|
123 // 2) the parser is active - waiting to for its RunL() to be called. |
|
124 if( iDataState == EWaitingForData || IsActive() ) |
|
125 { |
|
126 // Cancel and do the reset. |
|
127 Cancel(); |
|
128 DoReset(); |
|
129 } |
|
130 else |
|
131 { |
|
132 // Debug check for a double Reset() call... |
|
133 __ASSERT_DEBUG( iDataState != EReset, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicDoubleReset) ); |
|
134 |
|
135 // The Reset() was called inside a callback - defer resetting the parser |
|
136 // until call stack back in the parser RunL(). |
|
137 iDataState = EReset; |
|
138 } |
|
139 } |
|
140 |
|
141 void CHttpMessageParser::CompleteSelf() |
|
142 /** |
|
143 Self-complete function. Ensures that the state machine is processed. |
|
144 */ |
|
145 { |
|
146 TRequestStatus* pStat = &iStatus; |
|
147 User::RequestComplete(pStat, KErrNone); |
|
148 SetActive(); |
|
149 } |
|
150 |
|
151 void CHttpMessageParser::DoReset() |
|
152 /** |
|
153 Resets the parser. The parser moves into the Idle state. Allocated resources |
|
154 are also reset. |
|
155 */ |
|
156 { |
|
157 // Reset the parser - parser state should be Idle and the data state should |
|
158 // be WaitingForData. |
|
159 iParserState = EIdle; |
|
160 iDataState = EWaitingForData; |
|
161 |
|
162 // Reset the data parser |
|
163 iDataParser.Reset(); |
|
164 } |
|
165 |
|
166 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseStartLineL() |
|
167 /** |
|
168 Parses for the start-line. The start-line is delimited by the first eol |
|
169 marker. No further parsing of the start-line is done by the parser - it is |
|
170 left to the observer to parse for either a request-line or a status-line. |
|
171 The observer is informed of the start-line. |
|
172 @return The parsing status for the start-line. The value ESectionDone |
|
173 indicates the start-line has been parsed. The value EBufferEmpty |
|
174 indicates that more message data is required to parse the |
|
175 start-line. |
|
176 @panic EHttpMessagePanicBadDataParserResult An unexpected status was |
|
177 returned by the data |
|
178 parser. |
|
179 */ |
|
180 { |
|
181 // Get line from the parser |
|
182 TPtrC8 startLine; |
|
183 THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(startLine); |
|
184 |
|
185 // Check the parse status... |
|
186 TParsingStatus startlineStatus = ESectionNotDone; |
|
187 switch( parseResult ) |
|
188 { |
|
189 case THttpDataParser::ELineParsed: |
|
190 { |
|
191 // The start-line has been parsed - inform the observer. |
|
192 iObserver.StartLineL(startLine); |
|
193 startlineStatus = ESectionDone; |
|
194 } break; |
|
195 case THttpDataParser::EPartialData: |
|
196 { |
|
197 // Not all of start-line received - need more data. |
|
198 startlineStatus = EBufferEmpty; |
|
199 } break; |
|
200 case THttpDataParser::EEmptyLine: |
|
201 { |
|
202 // Although blank-lines are not allowed, be tolerent. Need to parse for |
|
203 // the start-line again. The start-line is not complete - use default |
|
204 // return value. |
|
205 } break; |
|
206 default: |
|
207 // This covers EGotData case. |
|
208 THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult); |
|
209 break; |
|
210 } |
|
211 return startlineStatus; |
|
212 } |
|
213 |
|
214 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseSingleHeaderL() |
|
215 /** |
|
216 Parses for a header field line. A header field line is delimited by an eol |
|
217 marker. If an empty line is parser then this indicates the end of the header |
|
218 fields section. If a header field is found then the field name and field |
|
219 value are extracted. The observer is passed the field name and value. |
|
220 |
|
221 If a malformed header field is found (ie the colon ':' is missing) then that |
|
222 line is ignored. The function will leave if an error occurs in removing the |
|
223 leading and trailing whitespace from the parsed header field name. |
|
224 |
|
225 Note that the header field value may be empty. |
|
226 @return The parsing status for the header fields section. The value |
|
227 ESectionDone indicates that an empty line has been parsed which |
|
228 means that all the header fields have been parsed. The value |
|
229 ESectionNotDone indicates that a header field has been found. |
|
230 The value EBufferEmpty indicates that more message data is |
|
231 required to parse the header fields section. |
|
232 @panic EHttpMessagePanicBadDataParserResult An unexpected status was |
|
233 returned by the data |
|
234 parser. |
|
235 */ |
|
236 { |
|
237 // Get line from the parser |
|
238 TPtrC8 line; |
|
239 THttpDataParser::TParseResult parseResult = iDataParser.GetHeaderLineL(line); |
|
240 |
|
241 // Check the parse status... |
|
242 TParsingStatus headerStatus = ESectionNotDone; |
|
243 switch( parseResult ) |
|
244 { |
|
245 case THttpDataParser::ELineParsed: |
|
246 { |
|
247 // Got a header field - find the field name and value. |
|
248 TInt colonPos = line.Locate(KColon); |
|
249 |
|
250 if( colonPos == KErrNotFound ) |
|
251 { |
|
252 // No colon - syntax error. Be robust and ignore the this line. |
|
253 // There are still more headers to find - use default return value. |
|
254 break; |
|
255 } |
|
256 |
|
257 // Found the field name and value |
|
258 TPtrC8 name = line.Left(colonPos); |
|
259 TPtrC8 value = line.Mid(colonPos + 1); // move past the colon |
|
260 |
|
261 // Remove any leading/trailing whitespace but if it is empty, try to be robust and continue with the next one. |
|
262 TInt rwsErr = InetProtTextUtils::RemoveWhiteSpace(name, InetProtTextUtils::ERemoveBoth); |
|
263 |
|
264 // Field value may be empty - e.g. "host:". Ignore any returned error. |
|
265 InetProtTextUtils::RemoveWhiteSpace(value, InetProtTextUtils::ERemoveBoth); |
|
266 |
|
267 // Pass the header field name and value to the observer if it seemed valid. |
|
268 if( rwsErr == KErrNone ) |
|
269 { |
|
270 iObserver.HeaderL(name, value); |
|
271 } |
|
272 |
|
273 // There are still more headers to find - use default return value. |
|
274 } break; |
|
275 case THttpDataParser::EPartialData: |
|
276 { |
|
277 // Not all of header field received - need more data. |
|
278 headerStatus = EBufferEmpty; |
|
279 } break; |
|
280 case THttpDataParser::EEmptyLine: |
|
281 { |
|
282 // Reached the end of the headers section - move on. |
|
283 headerStatus = ESectionDone; |
|
284 } break; |
|
285 default: |
|
286 // This covers EGotData case. |
|
287 THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult); |
|
288 break; |
|
289 } |
|
290 return headerStatus; |
|
291 } |
|
292 |
|
293 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseHeadersL() |
|
294 { |
|
295 // Do parsing of 5 headers without AO scheduling |
|
296 TParsingStatus headerStatus = ParseSingleHeaderL(); |
|
297 if(headerStatus == ESectionNotDone) |
|
298 { |
|
299 headerStatus = ParseSingleHeaderL(); |
|
300 if(headerStatus == ESectionNotDone) |
|
301 { |
|
302 headerStatus = ParseSingleHeaderL(); |
|
303 if(headerStatus == ESectionNotDone) |
|
304 { |
|
305 headerStatus = ParseSingleHeaderL(); |
|
306 if(headerStatus == ESectionNotDone) |
|
307 { |
|
308 headerStatus = ParseSingleHeaderL(); |
|
309 } |
|
310 } |
|
311 } |
|
312 } |
|
313 return headerStatus; |
|
314 } |
|
315 |
|
316 CHttpMessageParser::TParsingStatus CHttpMessageParser::ReadBodyData(TPtrC8& aData) |
|
317 /** |
|
318 Reads the entity body data from the current data packet. As the entity body |
|
319 data can be segmented by the tranport layer this function may need to be |
|
320 called more than once. The remaining amount of entity body data is updated. |
|
321 @param aData An output argument set the entity body data extracted |
|
322 from the current data packet. |
|
323 @return The parsing status for the entity body. The value ESectionDone |
|
324 indicates the entity body has been extracted. The value |
|
325 EBufferEmpty indicates that more message data is required to |
|
326 extract the entity body. |
|
327 @panic EHttpMessagePanicBadDataParserResult An unexpected status was |
|
328 returned by the data |
|
329 parser. |
|
330 @panic EInvariantFalse The data parser returned a status that conflicts |
|
331 with the amount of entity body data to extract. |
|
332 */ |
|
333 { |
|
334 // If the body length is unknown then set the data size to a maximum value |
|
335 if(iDataSizeLeft==-1) |
|
336 iDataSizeLeft = KMaxTInt; |
|
337 |
|
338 // Get the remaining body data from the data parser |
|
339 THttpDataParser::TParseResult parseResult = iDataParser.GetData(aData, iDataSizeLeft); |
|
340 |
|
341 // Update the size of the data left to get |
|
342 iDataSizeLeft -= aData.Length(); |
|
343 |
|
344 // Check the parse status... |
|
345 TParsingStatus dataStatus = EBufferEmpty; |
|
346 switch( parseResult ) |
|
347 { |
|
348 case THttpDataParser::EGotData: |
|
349 { |
|
350 __ASSERT_DEBUG( iDataSizeLeft == 0, User::Invariant() ); |
|
351 |
|
352 // Got all the data - section is done. |
|
353 dataStatus = ESectionDone; |
|
354 } break; |
|
355 case THttpDataParser::EPartialData: |
|
356 { |
|
357 __ASSERT_DEBUG( iDataSizeLeft > 0, User::Invariant() ); |
|
358 |
|
359 // Use the default value... |
|
360 } break; |
|
361 default: |
|
362 // This covers ELineParsed and EEmptyLine cases. |
|
363 THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult); |
|
364 break; |
|
365 } |
|
366 return dataStatus; |
|
367 } |
|
368 |
|
369 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseChunkSizeL() |
|
370 /** |
|
371 Parses for a chunk-size component. The chunk-size component specifies the |
|
372 size of following chunk-data component. As the chunk-size can be segmented |
|
373 at the transport layer this function may need to called several times for |
|
374 the same chunk-size component. |
|
375 |
|
376 Section 3.6.1 in RFC2616 defines the following - |
|
377 chunk = chunk-size [ chunk-extension ] CRLF |
|
378 chunk-data CRLF |
|
379 chunk-size = 1*HEX |
|
380 chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) |
|
381 |
|
382 The chunk-size component is delimited by the eol marker. There can be an |
|
383 optional chunk-extension component between the chunk-size and the eol marker. |
|
384 This is parsed but ignored - a leading ';' is not checked for. Also, if an |
|
385 empty line is parsed this is also ignored. The function will also leave if |
|
386 there is an error in converting the hex number to its numeric value. |
|
387 @return The parsing status for this chunk-size component. The value |
|
388 ESectionDone indicates that all the chunk-data has been parsed |
|
389 and the value EBufferEmpty indicates that more message data is |
|
390 required to parse this chunk-size component. |
|
391 @leave THttpDataParser::GetLineL |
|
392 @panic EHttpMessagePanicBadDataParserResult An unexpected status was |
|
393 returned by the data |
|
394 parser. |
|
395 */ |
|
396 { |
|
397 // Get line from the parser |
|
398 TPtrC8 line; |
|
399 THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(line); |
|
400 |
|
401 // Check the parse status... |
|
402 TParsingStatus sizeStatus = ESectionDone; |
|
403 switch( parseResult ) |
|
404 { |
|
405 case THttpDataParser::ELineParsed: |
|
406 { |
|
407 // Ok got the line - find the hex number defining the chunk-size. |
|
408 TInt val = InetProtTextUtils::ConvertDescriptorToHex(line, iDataSizeLeft); |
|
409 if( val < 0 ) |
|
410 { |
|
411 // Error: invalid data.. ignore it. |
|
412 sizeStatus = ESectionDone; |
|
413 } |
|
414 // Do not care about the chunk-extension - do not bother to check that |
|
415 // it is well formed. |
|
416 |
|
417 // The chunk-size has been parsed - use default return value. |
|
418 } break; |
|
419 case THttpDataParser::EPartialData: |
|
420 { |
|
421 // Not all of start-line received - need more data. |
|
422 sizeStatus = EBufferEmpty; |
|
423 } break; |
|
424 case THttpDataParser::EEmptyLine: |
|
425 { |
|
426 // The chunk-size was expected - got an empty line. Ignore. |
|
427 sizeStatus = ESectionNotDone; |
|
428 } break; |
|
429 default: |
|
430 // This covers EGotData case. |
|
431 THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult); |
|
432 break; |
|
433 } |
|
434 return sizeStatus; |
|
435 } |
|
436 |
|
437 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseChunkDataL(TPtrC8& aData) |
|
438 /** |
|
439 Parses for a chunk-data component. The preceeding chunk-size component |
|
440 specified the length of this chunk-data component. As the chunk-data can be |
|
441 segmented at the transport layer this function may need to called several |
|
442 times for the same chunk-data component. |
|
443 |
|
444 Section 3.6.1 in RFC2616 defines the following - |
|
445 chunk = chunk-size [ chunk-extension ] CRLF |
|
446 chunk-data CRLF |
|
447 |
|
448 The chunk-data component is delimited by the eol marker. If the amount of |
|
449 chunk-data does not match the amount defined by the chunk-size then the |
|
450 function will leave with KErrCorrupt. |
|
451 @param aData An output argument set to the chunk-data parsed. |
|
452 @return The parsing status for this chunk-data component. The value |
|
453 ESectionDone indicates that all the chunk-data has been parsed |
|
454 and the value EBufferEmpty indicates that more message data is |
|
455 required to parse this chunk-data component. |
|
456 @leave KErrCorrupt An incorrect amount of chunk-data was parsed. |
|
457 @panic EInvariantFalse The body data extraction indicated that it had |
|
458 extracted all the body data but the parser still |
|
459 expected more. |
|
460 @panic EHttpMessagePanicBadDataParserResult An unexpected status was |
|
461 returned by the data |
|
462 parser. |
|
463 */ |
|
464 { |
|
465 // Has the data-chunk been read? Check iDataSizeLeft. |
|
466 TParsingStatus dataStatus = ESectionDone; |
|
467 if( iDataSizeLeft > 0 ) |
|
468 { |
|
469 // There is chunk-data to extract |
|
470 dataStatus = ReadBodyData(aData); |
|
471 } |
|
472 |
|
473 // Does the end CRLF need to be parsed? The chunk-data may have been found. |
|
474 if( dataStatus == ESectionDone ) |
|
475 { |
|
476 __ASSERT_DEBUG( iDataSizeLeft == 0, User::Invariant() ); |
|
477 |
|
478 // Need to extract an empty-line |
|
479 TPtrC8 line; |
|
480 THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(line); |
|
481 |
|
482 // Check the parse status... |
|
483 switch( parseResult ) |
|
484 { |
|
485 case THttpDataParser::EEmptyLine: |
|
486 { |
|
487 // Got the CRLF - section is done. Use default value. |
|
488 } break; |
|
489 case THttpDataParser::EPartialData: |
|
490 { |
|
491 dataStatus = EBufferEmpty; |
|
492 } break; |
|
493 case THttpDataParser::ELineParsed: |
|
494 { |
|
495 // There was more data than the chunk-size specified. |
|
496 |
|
497 // Error ignored to add robustness against problematic real-world servers. |
|
498 //User::Leave(KErrCorrupt); |
|
499 } break; |
|
500 default: |
|
501 // This covers EGotData case. |
|
502 THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult); |
|
503 break; |
|
504 } |
|
505 } |
|
506 return dataStatus; |
|
507 } |
|
508 |
|
509 /* |
|
510 * Methods from MHttpDataParserObserver |
|
511 */ |
|
512 |
|
513 void CHttpMessageParser::ReAllocBufferL(TInt aRequiredSize, TPtr8& aBuffer) |
|
514 /** |
|
515 Reallocates the line buffer. The parser supplies the line buffer to the data |
|
516 parser. The data parser needs more space to store the current line. If the |
|
517 line buffer has not been created then it is created, otherwise it is |
|
518 reallocated to at least the required size. |
|
519 @param aRequiredSize The minimum size of buffer required. |
|
520 @param aBuffer An output argument set to the reallocated buffer. |
|
521 @panic EInvariantFalse The required size was less then the current max |
|
522 size of the buffer. |
|
523 */ |
|
524 { |
|
525 if( iLineBuffer == NULL ) |
|
526 { |
|
527 // Create the buffer.. |
|
528 iLineBuffer = HBufC8::NewL(aRequiredSize + KDefaultBufferSize); |
|
529 } |
|
530 else |
|
531 { |
|
532 __ASSERT_DEBUG( aRequiredSize > iLineBuffer->Des().MaxLength(), User::Invariant() ); |
|
533 |
|
534 iLineBuffer = iLineBuffer->ReAllocL(aRequiredSize + KDefaultBufferSize); |
|
535 } |
|
536 aBuffer.Set(iLineBuffer->Des()); |
|
537 } |
|
538 |
|
539 void CHttpMessageParser::DeleteBuffer() |
|
540 /** |
|
541 Deletes the line buffer. |
|
542 @internalComponent |
|
543 */ |
|
544 { |
|
545 delete iLineBuffer; |
|
546 iLineBuffer = NULL; |
|
547 } |
|
548 |
|
549 /* |
|
550 * Methods from CActive |
|
551 */ |
|
552 void CHttpMessageParser::RunL() |
|
553 /** |
|
554 Asynchronous request service handler. The parser state machine is processed |
|
555 in this function. Behaviour depends on the state. The parser will self- |
|
556 complete if the current data packet has unparsed data. If all the data in |
|
557 the current data packet has been parsed then the parser suspends its state |
|
558 machine and waits for the observer to notify it when there is more data |
|
559 available. If the observer has reset the parser in one of the callback then |
|
560 the parser will defer resetting itself until it is back in the RunL(). |
|
561 @panic EHttpMessagePanicBadParserState The parser state machine was in |
|
562 a illegal state. |
|
563 @panic EHttpMessagePanicBadDataState The parser was in the incorrect |
|
564 data state. |
|
565 @panic EHttpMessagePanicBadBodySize The observer specified an unknown |
|
566 non-positive body size. |
|
567 */ |
|
568 { |
|
569 __ASSERT_DEBUG( iDataState == EGotData, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) ); |
|
570 TParsingStatus status = ESectionNotDone; |
|
571 switch( iParserState ) |
|
572 { |
|
573 case EIdle: |
|
574 { |
|
575 // Move to the ParsingStartLine and set the parsing status to move on. |
|
576 iParserState = EParsingStartLine; |
|
577 status = ESectionDone; |
|
578 } |
|
579 //coverity [MISSING_BREAK] |
|
580 // Fallthrough is required here as we no longer parse line by line. We parse startline & headers (5) at one go. |
|
581 case EParsingStartLine: |
|
582 { |
|
583 status = ParseStartLineL(); |
|
584 |
|
585 // Has the start-line been parsed? |
|
586 if( status == ESectionDone ) |
|
587 { |
|
588 // Start-line has been parsed - parse for headers next. |
|
589 iParserState = EParsingHeaders; |
|
590 } |
|
591 else |
|
592 { |
|
593 break; //Start line is not parsed yet as we haven't received the complete data. Break and wait for more data. |
|
594 } |
|
595 } |
|
596 case EParsingHeaders: |
|
597 { |
|
598 status = ParseHeadersL(); |
|
599 |
|
600 if( status == ESectionDone ) |
|
601 { |
|
602 // No more headers - obtain entity body size from the observer. |
|
603 iDataSizeLeft = iObserver.BodySizeL(); |
|
604 switch( iDataSizeLeft ) |
|
605 { |
|
606 case MHttpMessageParserObserver::EChunked: |
|
607 { |
|
608 // Chunk-encoded body. |
|
609 iParserState = EParsingChunkSize; |
|
610 } break; |
|
611 case MHttpMessageParserObserver::EUnknown: |
|
612 { |
|
613 // for HTTP/1.0-style responses |
|
614 iParserState = EReadingBodyData; |
|
615 } break; |
|
616 case MHttpMessageParserObserver::ENoBody: |
|
617 { |
|
618 // No body expected - message is complete. |
|
619 iParserState = EMessageComplete; |
|
620 } break; |
|
621 default: |
|
622 __ASSERT_DEBUG( iDataSizeLeft > 0, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadBodySize) ); |
|
623 |
|
624 // Non-encoded body. |
|
625 iParserState = EReadingBodyData; |
|
626 break; |
|
627 } |
|
628 } |
|
629 } break; |
|
630 case EParsingChunkSize: |
|
631 { |
|
632 status = ParseChunkSizeL(); |
|
633 |
|
634 if( status == ESectionDone ) |
|
635 { |
|
636 // Need to check the chunk size for last-chunk |
|
637 if( iDataSizeLeft == 0 ) |
|
638 { |
|
639 // Recieved a chunk of size 0, inform the observer. |
|
640 TPtrC8 data; |
|
641 iObserver.BodyChunkL(data); |
|
642 |
|
643 // Notify the observer that the body has been received |
|
644 iObserver.BodyCompleteL(); |
|
645 |
|
646 // Received the last-chunk token - now parse for trailers. |
|
647 iParserState = EParsingTrailerHeaders; |
|
648 } |
|
649 else |
|
650 { |
|
651 // Still expecting chunk-data... |
|
652 iParserState = EReadingChunkData; |
|
653 } |
|
654 } |
|
655 } break; |
|
656 case EParsingTrailerHeaders: |
|
657 { |
|
658 status = ParseHeadersL(); |
|
659 |
|
660 if( status == ESectionDone ) |
|
661 { |
|
662 // All the trailers have been found. |
|
663 iParserState = EMessageComplete; |
|
664 } |
|
665 } break; |
|
666 case EReadingBodyData: |
|
667 { |
|
668 // Read the body data... |
|
669 TPtrC8 data; |
|
670 status = ReadBodyData(data); |
|
671 |
|
672 // Only inform the observer if there is any data. |
|
673 if( data.Length() > 0 ) |
|
674 { |
|
675 iObserver.BodyChunkL(data); |
|
676 } |
|
677 // Has all the body data been received - need to check that the parser |
|
678 // has not been reset. |
|
679 if( status == ESectionDone && iDataState != EReset ) |
|
680 { |
|
681 // Notify the observer that the body has been received |
|
682 iObserver.BodyCompleteL(); |
|
683 |
|
684 // Have received all the body data - message is complete. |
|
685 iParserState = EMessageComplete; |
|
686 } |
|
687 } break; |
|
688 case EReadingChunkData: |
|
689 { |
|
690 // Read the chunk-data... |
|
691 TPtrC8 data; |
|
692 status = ParseChunkDataL(data); |
|
693 |
|
694 // Only inform the observer if there is any data. |
|
695 if( data.Length() ) |
|
696 { |
|
697 iObserver.BodyChunkL(data); |
|
698 } |
|
699 |
|
700 if( status == ESectionDone ) |
|
701 { |
|
702 // Have received all the body data - get the size of the next chunk. |
|
703 iParserState = EParsingChunkSize; |
|
704 } |
|
705 } break; |
|
706 case EMessageComplete: |
|
707 { |
|
708 // Message complete - is there any excess data? |
|
709 TPtrC8 data; |
|
710 iDataParser.UnparsedData(data); |
|
711 |
|
712 // Notify the observer that the message is complete. |
|
713 iObserver.MessageCompleteL(data); |
|
714 |
|
715 // Move to the Idle state and set the parsing status to Stop. |
|
716 iParserState = EIdle; |
|
717 status = EStop; |
|
718 } break; |
|
719 default: |
|
720 THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadParserState); |
|
721 break; |
|
722 } |
|
723 // Determine the next action... |
|
724 if( iDataState == EReset ) |
|
725 { |
|
726 // The observer has reset the parser during one of the callbacks - it is |
|
727 // now safe to do the reset. |
|
728 DoReset(); |
|
729 } |
|
730 else if( status == EBufferEmpty || status == EStop ) |
|
731 { |
|
732 // Expect more data before being able to continue parsing. |
|
733 iDataState = EWaitingForData; |
|
734 |
|
735 // The current message component cannot be parsed due to lack of data, |
|
736 // or the parsing has stopped. Need to release the current data packet |
|
737 // (and wait for the next one in the case of EBufferEmpty). |
|
738 iObserver.ReleaseDataPacket(); |
|
739 } |
|
740 else |
|
741 { |
|
742 // Here status is ESectionNotDone or ESectionDone - either the current |
|
743 // message component has been completed and the parser needs to move |
|
744 // onto the next component, or the current component still needs to |
|
745 // complete. In both cases, self-complete to continue parsing. |
|
746 CompleteSelf(); |
|
747 } |
|
748 } |
|
749 |
|
750 void CHttpMessageParser::DoCancel() |
|
751 /** |
|
752 Asynchronous request cancel. This function does nothing as the only asynch |
|
753 request that is made is a self-complete request. |
|
754 */ |
|
755 { |
|
756 // Do nothing... |
|
757 } |
|
758 |
|
759 TInt CHttpMessageParser::RunError(TInt aError) |
|
760 /** |
|
761 Asynchronous request service error handler. An error has occured whilst |
|
762 processing the state machine. Reset the parser and notify the observer of |
|
763 the error. |
|
764 @param aError The error code. |
|
765 @return An interger value of KErrNone indicates that the error has been |
|
766 handled. |
|
767 @post The parser has been reset. |
|
768 */ |
|
769 { |
|
770 // Stop parsing and reset. |
|
771 DoReset(); |
|
772 |
|
773 // Notify the observer of the error |
|
774 return iObserver.HandleParserError(aError); |
|
775 } |
|
776 |
|
777 void CHttpMessageParser::Flush () |
|
778 { |
|
779 // Say a response like this. |
|
780 // HTTP/1.0 302 Moves\nStatus: 302 Moved\nPragma: no-cache\nLocation: http://127.0.0.1\n |
|
781 // In this response, there is no empty line after the header. Parser cannot parse the last header if |
|
782 // it didn't find an empty line after the header. Force the parser to parse the last header line. |
|
783 if ( iDataState == EWaitingForData && iParserState == EParsingHeaders ) |
|
784 { |
|
785 iDataParser.SetData ( KLineFeed() ); |
|
786 TRAP_IGNORE( ParseHeadersL () ); |
|
787 } |
|
788 // Message is completed but observer notification hasn't happened |
|
789 // Notify now with uparsed data. |
|
790 if ( iParserState == EMessageComplete ) |
|
791 { |
|
792 TPtrC8 unparsedData; |
|
793 iDataParser.UnparsedData(unparsedData); |
|
794 iObserver.MessageCompleteL(unparsedData); |
|
795 iParserState = EIdle; |
|
796 } |
|
797 } |
|
798 |
|
799 // Completes parsing of the message. Header parts has been parsed. |
|
800 // Complete the body part of the message. This happens in case of 3xx response |
|
801 // where HTTP FW parses the headers and notify the client/filters. The client/filter |
|
802 // will cancel the transaction and resubmit on the new URL. |
|
803 TBool CHttpMessageParser::CompleteMessage ( const TDesC8& aData ) |
|
804 { |
|
805 // Set the parser data |
|
806 if ( aData.Length () > 0 ) |
|
807 iDataParser.SetData ( aData ); |
|
808 |
|
809 // We are reading body data. A content length value is known. |
|
810 if ( iParserState == EReadingBodyData ) |
|
811 { |
|
812 TPtrC8 data; |
|
813 if ( ReadBodyData(data) == ESectionDone ) |
|
814 { |
|
815 iObserver.BodyCompleteL(); |
|
816 iParserState = EMessageComplete; |
|
817 } |
|
818 } |
|
819 // Read the chunked response. |
|
820 if ( iParserState == EReadingChunkData || iParserState == EParsingChunkSize |
|
821 || iParserState == EParsingTrailerHeaders ) |
|
822 { |
|
823 TParsingStatus status = ESectionNotDone; |
|
824 // This tiny look executes till the buffer is empty or the parser state is EMessageComplete |
|
825 while ( status != EBufferEmpty && iParserState != EMessageComplete ) |
|
826 { |
|
827 switch ( iParserState ) |
|
828 { |
|
829 case EReadingChunkData: |
|
830 { |
|
831 TPtrC8 data; |
|
832 status = ParseChunkDataL(data); |
|
833 if( status == ESectionDone ) |
|
834 { |
|
835 iParserState = EParsingChunkSize; |
|
836 } |
|
837 } |
|
838 break; |
|
839 case EParsingChunkSize: |
|
840 status = ParseChunkSizeL(); |
|
841 if ( status == ESectionDone ) |
|
842 iParserState = (iDataSizeLeft == 0) ? EParsingTrailerHeaders : EReadingChunkData; |
|
843 break; |
|
844 |
|
845 case EParsingTrailerHeaders: |
|
846 status = ParseHeadersL(); |
|
847 if ( status == ESectionDone ) |
|
848 iParserState = EMessageComplete; |
|
849 break; |
|
850 default: |
|
851 // Do nothing. |
|
852 break; |
|
853 } |
|
854 } |
|
855 } |
|
856 return iParserState == EMessageComplete; |
|
857 } |
|
858 |
|
859 |
|
860 |