| author | teknolog | 
| Wed, 05 May 2010 10:50:52 +0100 | |
| changeset 84 | fbb6c20a24b5 | 
| parent 81 | 46e6a623d36d | 
| child 90 | d0c0c3e6f7a1 | 
| permissions | -rw-r--r-- | 
| 2 | 1 | /* | 
| 2 | * Copyright (c) 2007-2010 Sebastian Brannstrom, Lars Persson, EmbedDev AB | |
| 3 | * | |
| 4 | * All rights reserved. | |
| 5 | * This component and the accompanying materials are made available | |
| 6 | * under the terms of the License "Eclipse Public License v1.0" | |
| 7 | * which accompanies this distribution, and is available | |
| 8 | * at the URL "http://www.eclipse.org/legal/epl-v10.html". | |
| 9 | * | |
| 10 | * Initial Contributors: | |
| 11 | * EmbedDev AB - initial contribution. | |
| 12 | * | |
| 13 | * Contributors: | |
| 14 | * | |
| 15 | * Description: | |
| 16 | * | |
| 17 | */ | |
| 18 | ||
| 19 | // HttpEventHandler.cpp | |
| 20 | #include <e32debug.h> | |
| 21 | #include <httperr.h> | |
| 22 | #include "HttpEventHandler.h" | |
| 23 | #include "bautils.h" | |
| 24 | #include "Httpclient.h" | |
| 25 | ||
| 26 | void CHttpEventHandler::ConstructL() | |
| 27 | 	{
 | |
| 28 | //iVerbose = ETrue; | |
| 29 | } | |
| 30 | ||
| 31 | ||
| 32 | CHttpEventHandler::CHttpEventHandler(CHttpClient* aClient, MHttpClientObserver &aCallbacks, RFs& aFs): | |
| 33 | iFileServ(aFs), iHttpClient(aClient), iCallbacks(aCallbacks) | |
| 34 | 	{
 | |
| 35 | } | |
| 36 | ||
| 37 | ||
| 38 | CHttpEventHandler::~CHttpEventHandler() | |
| 39 | 	{	
 | |
| 40 | } | |
| 41 | ||
| 42 | ||
| 43 | CHttpEventHandler* CHttpEventHandler::NewLC(CHttpClient* aClient, MHttpClientObserver &aCallbacks, RFs& aFs) | |
| 44 | 	{
 | |
| 45 | CHttpEventHandler* me = new(ELeave)CHttpEventHandler(aClient, aCallbacks, aFs); | |
| 46 | CleanupStack::PushL(me); | |
| 47 | me->ConstructL(); | |
| 48 | return me; | |
| 49 | } | |
| 50 | ||
| 51 | ||
| 52 | CHttpEventHandler* CHttpEventHandler::NewL(CHttpClient* aClient, MHttpClientObserver &aCallbacks, RFs& aFs) | |
| 53 | 	{
 | |
| 54 | CHttpEventHandler* me = NewLC(aClient, aCallbacks, aFs); | |
| 55 | CleanupStack::Pop(me); | |
| 56 | return me; | |
| 57 | } | |
| 58 | ||
| 59 | void CHttpEventHandler::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent) | |
| 60 | 	{
 | |
| 61 | switch (aEvent.iStatus) | |
| 62 | 		{
 | |
| 63 | case THTTPEvent::EGotResponseHeaders: | |
| 64 | 			{
 | |
| 65 | // HTTP response headers have been received. We can determine now if there is | |
| 66 | // going to be a response body to save. | |
| 67 | RHTTPResponse resp = aTransaction.Response(); | |
| 68 | iLastStatusCode = resp.StatusCode(); | |
| 60 | 69 | 			DP1("Status: %d", iLastStatusCode);
 | 
| 2 | 70 | |
| 71 | // Dump the headers if we're being verbose | |
| 72 | //DumpRespHeadersL(aTransaction); | |
| 73 | ||
| 74 | // Determine if the body will be saved to disk | |
| 75 | iSavingResponseBody = ETrue; | |
| 76 | TBool cancelling = EFalse; | |
| 77 | if (resp.HasBody() && (iLastStatusCode >= 200) && (iLastStatusCode < 300) && (iLastStatusCode != 204)) | |
| 78 | 				{
 | |
| 79 | //iBytesDownloaded = 0; | |
| 80 | TInt dataSize = resp.Body()->OverallDataSize(); | |
| 81 | 				if (dataSize >= 0) {
 | |
| 82 | 					DP1("Response body size is %d", dataSize);
 | |
| 83 | iBytesTotal = dataSize; | |
| 84 | 				} else {
 | |
| 85 | 					DP("Response body size is unknown");
 | |
| 86 | iBytesTotal = -1; | |
| 87 | } | |
| 88 | iCallbacks.DownloadInfo(iHttpClient, dataSize); | |
| 89 | ||
| 90 | cancelling = EFalse; | |
| 91 | } | |
| 92 | ||
| 93 | // If we're cancelling, must do it now.. | |
| 94 | if (cancelling) | |
| 95 | 				{
 | |
| 96 | 				DP("Transaction Cancelled");
 | |
| 97 | aTransaction.Close(); | |
| 98 | iHttpClient->ClientRequestCompleteL(KErrCancel); | |
| 99 | } | |
| 100 | else if (iSavingResponseBody) // If we're saving, then open a file handle for the new file | |
| 101 | 				{
 | |
| 102 | iFileServ.Parse(iFileName, iParsedFileName); | |
| 103 | TInt valid = iFileServ.IsValidName(iFileName); | |
| 60 | 104 | |
| 2 | 105 | if (!valid) | 
| 106 | 					{
 | |
| 107 | 					DP("The specified filename is not valid!.");
 | |
| 108 | iSavingResponseBody = EFalse; | |
| 60 | 109 | iHttpClient->ClientRequestCompleteL(KErrBadName); | 
| 2 | 110 | } | 
| 111 | else | |
| 112 | 					{
 | |
| 113 | 					if (iContinue) {
 | |
| 114 | TInt err = iRespBodyFile.Open(iFileServ, iParsedFileName.FullName(),EFileWrite); | |
| 115 | if (err) | |
| 116 | 							{
 | |
| 60 | 117 | 							DP2("There was an error opening file '%S', err=%d", &iParsedFileName.FullName(), err);
 | 
| 2 | 118 | iSavingResponseBody = EFalse; | 
| 60 | 119 | iHttpClient->ClientRequestCompleteL(KErrInUse); | 
| 2 | 120 | User::Leave(err); | 
| 60 | 121 | } | 
| 122 | else | |
| 123 | 							{
 | |
| 2 | 124 | int pos = -KByteOverlap; | 
| 60 | 125 | if((err=iRespBodyFile.Seek(ESeekEnd, pos)) != KErrNone) | 
| 126 | 								{
 | |
| 2 | 127 | 								DP("Failed to set position!");
 | 
| 60 | 128 | iHttpClient->ClientRequestCompleteL(KErrWrite); | 
| 2 | 129 | User::Leave(err); | 
| 60 | 130 | } | 
| 131 | iBytesDownloaded = (pos > 0) ? pos : 0; | |
| 132 | iBytesTotal += iBytesDownloaded; | |
| 133 | 						DP1("Total bytes is now %u", iBytesTotal);
 | |
| 134 | 						DP1("Seeking end: %d", pos);
 | |
| 2 | 135 | } | 
| 60 | 136 | } | 
| 137 | else | |
| 138 | 						{
 | |
| 2 | 139 | TInt err = iRespBodyFile.Replace(iFileServ, | 
| 140 | iParsedFileName.FullName(), | |
| 141 | EFileWrite); | |
| 142 | if (err) | |
| 143 | 							{
 | |
| 144 | 							DP("There was an error replacing file");
 | |
| 145 | iSavingResponseBody = EFalse; | |
| 146 | User::Leave(err); | |
| 147 | } | |
| 148 | } | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | } break; | |
| 153 | case THTTPEvent::EGotResponseBodyData: | |
| 154 | 			{
 | |
| 155 | // Get the body data supplier | |
| 156 | iRespBody = aTransaction.Response().Body(); | |
| 157 | ||
| 158 | // Some (more) body data has been received (in the HTTP response) | |
| 159 | //DumpRespBody(aTransaction); | |
| 160 | 			//DP1("Saving: %d", iSavingResponseBody);
 | |
| 161 | // Append to the output file if we're saving responses | |
| 162 | if (iSavingResponseBody) | |
| 163 | 				{
 | |
| 164 | TPtrC8 bodyData; | |
| 165 | iRespBody->GetNextDataPart(bodyData); | |
| 166 | iBytesDownloaded += bodyData.Length(); | |
| 167 | TInt error = iRespBodyFile.Write(bodyData); | |
| 60 | 168 | |
| 2 | 169 | // on writing error we close connection | 
| 170 | 				if (error != KErrNone) {
 | |
| 60 | 171 | iRespBodyFile.Close(); | 
| 2 | 172 | iCallbacks.FileError(error); | 
| 173 | iHttpClient->ClientRequestCompleteL(error); | |
| 174 | return; | |
| 175 | } | |
| 176 | ||
| 177 | 				if (!iSilent) {
 | |
| 178 | iCallbacks.Progress(iHttpClient, iBytesDownloaded, iBytesTotal); | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | // Done with that bit of body data | |
| 183 | iRespBody->ReleaseData(); | |
| 184 | } break; | |
| 185 | case THTTPEvent::EResponseComplete: | |
| 186 | 			{
 | |
| 187 | // The transaction's response is complete | |
| 188 | ||
| 189 | 			DP("Transaction Complete");
 | |
| 190 | 			DP("Closing file");
 | |
| 191 | iRespBodyFile.Close(); | |
| 192 | } break; | |
| 193 | case THTTPEvent::ESucceeded: | |
| 194 | 			{
 | |
| 195 | 			DP("Transaction Successful");
 | |
| 81 
46e6a623d36d
Fix to make sure file handle is closed on failed download.
 teknolog parents: 
60diff
changeset | 196 | iRespBodyFile.Close(); | 
| 2 | 197 | aTransaction.Close(); | 
| 198 | iHttpClient->ClientRequestCompleteL(KErrNone); | |
| 199 | } break; | |
| 200 | case THTTPEvent::EFailed: | |
| 201 | 			{
 | |
| 202 | 			DP("Transaction Failed");
 | |
| 81 
46e6a623d36d
Fix to make sure file handle is closed on failed download.
 teknolog parents: 
60diff
changeset | 203 | iRespBodyFile.Close(); | 
| 2 | 204 | aTransaction.Close(); | 
| 205 | ||
| 206 | if(iLastStatusCode == HTTPStatus::EOk || iLastStatusCode == HTTPStatus::ECreated || iLastStatusCode == HTTPStatus::EAccepted) | |
| 207 | 				{
 | |
| 208 | iLastStatusCode = KErrNone; | |
| 209 | } | |
| 210 | ||
| 211 | iHttpClient->ClientRequestCompleteL(iLastStatusCode); | |
| 212 | } break; | |
| 213 | case THTTPEvent::ERedirectedPermanently: | |
| 214 | 			{
 | |
| 215 | 			DP("Permanent Redirection");
 | |
| 216 | } break; | |
| 217 | case THTTPEvent::ERedirectedTemporarily: | |
| 218 | 			{
 | |
| 219 | 			DP("Temporary Redirection");
 | |
| 220 | } break; | |
| 221 | default: | |
| 222 | 			{
 | |
| 223 | 			DP1("<unrecognised event: %d>", aEvent.iStatus);
 | |
| 224 | // close off the transaction if it's an error | |
| 225 | if (aEvent.iStatus < 0) | |
| 226 | 				{
 | |
| 81 
46e6a623d36d
Fix to make sure file handle is closed on failed download.
 teknolog parents: 
60diff
changeset | 227 | iRespBodyFile.Close(); | 
| 2 | 228 | aTransaction.Close(); | 
| 229 | iHttpClient->ClientRequestCompleteL(aEvent.iStatus); | |
| 230 | } | |
| 231 | } break; | |
| 232 | } | |
| 233 | } | |
| 234 | ||
| 60 | 235 | TInt CHttpEventHandler::MHFRunError(TInt aError, RHTTPTransaction aTransaction, const THTTPEvent& /*aEvent*/) | 
| 2 | 236 | 	{
 | 
| 237 | 	DP1("MHFRunError fired with error code %d", aError);
 | |
| 60 | 238 | aTransaction.Close(); | 
| 239 | TRAP_IGNORE(iHttpClient->ClientRequestCompleteL(aError)); | |
| 2 | 240 | return KErrNone; | 
| 241 | } | |
| 242 | ||
| 243 | void CHttpEventHandler::SetSaveFileName(const TDesC &fName, TBool aContinue) | |
| 244 | 	{
 | |
| 245 | iFileName.Copy(fName); | |
| 246 | iContinue = aContinue; | |
| 247 | } | |
| 248 | ||
| 249 | void CHttpEventHandler::DumpRespHeadersL(RHTTPTransaction& aTrans) | |
| 250 | 	{
 | |
| 251 | RHTTPResponse resp = aTrans.Response(); | |
| 252 | RStringPool strP = aTrans.Session().StringPool(); | |
| 253 | RHTTPHeaders hdr = resp.GetHeaderCollection(); | |
| 254 | THTTPHdrFieldIter it = hdr.Fields(); | |
| 255 | ||
| 256 | TBuf<KMaxHeaderNameLen> fieldName16; | |
| 257 | TBuf<KMaxHeaderValueLen> fieldVal16; | |
| 258 | ||
| 259 | while (it.AtEnd() == EFalse) | |
| 260 | 		{
 | |
| 261 | RStringTokenF fieldName = it(); | |
| 262 | RStringF fieldNameStr = strP.StringF(fieldName); | |
| 263 | THTTPHdrVal fieldVal; | |
| 264 | if (hdr.GetField(fieldNameStr,0,fieldVal) == KErrNone) | |
| 265 | 			{
 | |
| 266 | const TDesC8& fieldNameDesC = fieldNameStr.DesC(); | |
| 267 | fieldName16.Copy(fieldNameDesC.Left(KMaxHeaderNameLen)); | |
| 268 | switch (fieldVal.Type()) | |
| 269 | 				{
 | |
| 270 | case THTTPHdrVal::KTIntVal: | |
| 271 | 				DP2("%S: %d", &fieldName16, fieldVal.Int());
 | |
| 272 | break; | |
| 273 | case THTTPHdrVal::KStrFVal: | |
| 274 | 				{
 | |
| 275 | RStringF fieldValStr = strP.StringF(fieldVal.StrF()); | |
| 276 | const TDesC8& fieldValDesC = fieldValStr.DesC(); | |
| 277 | fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen)); | |
| 278 | 				DP2("%S: %S", &fieldName16, &fieldVal16);
 | |
| 279 | } | |
| 280 | break; | |
| 281 | case THTTPHdrVal::KStrVal: | |
| 282 | 				{
 | |
| 283 | RString fieldValStr = strP.String(fieldVal.Str()); | |
| 284 | const TDesC8& fieldValDesC = fieldValStr.DesC(); | |
| 285 | fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen)); | |
| 286 | 				DP2("%S: %S", &fieldName16, &fieldVal16);
 | |
| 287 | } | |
| 288 | break; | |
| 289 | case THTTPHdrVal::KDateVal: | |
| 290 | 				{
 | |
| 291 | TDateTime date = fieldVal.DateTime(); | |
| 292 | } | |
| 293 | break; | |
| 294 | default: | |
| 295 | 				DP1("%S: <unrecognised value type>", &fieldName16);
 | |
| 296 | break; | |
| 297 | } | |
| 298 | ||
| 299 | // Display realm for WWW-Authenticate header | |
| 300 | RStringF wwwAuth = strP.StringF(HTTP::EWWWAuthenticate,RHTTPSession::GetTable()); | |
| 301 | if (fieldNameStr == wwwAuth) | |
| 302 | 				{
 | |
| 303 | // check the auth scheme is 'basic' | |
| 304 | RStringF basic = strP.StringF(HTTP::EBasic,RHTTPSession::GetTable()); | |
| 305 | RStringF realm = strP.StringF(HTTP::ERealm,RHTTPSession::GetTable()); | |
| 306 | THTTPHdrVal realmVal; | |
| 307 | if ((fieldVal.StrF() == basic) && | |
| 308 | (!hdr.GetParam(wwwAuth, realm, realmVal))) | |
| 309 | 					{
 | |
| 310 | RStringF realmValStr = strP.StringF(realmVal.StrF()); | |
| 311 | fieldVal16.Copy(realmValStr.DesC()); | |
| 312 | 					DP1("Realm is: %S", &fieldVal16);
 | |
| 313 | } | |
| 314 | } | |
| 315 | } | |
| 316 | ++it; | |
| 317 | } | |
| 318 | } | |
| 319 | ||
| 320 | void CHttpEventHandler::DumpRespBody(RHTTPTransaction& aTrans) | |
| 321 | 	{
 | |
| 322 | MHTTPDataSupplier* body = aTrans.Response().Body(); | |
| 323 | TPtrC8 dataChunk; | |
| 324 | TBool isLast = body->GetNextDataPart(dataChunk); | |
| 325 | DumpIt(dataChunk); | |
| 326 | if (isLast) | |
| 327 | 		DP("Got last data chunk.");
 | |
| 328 | } | |
| 329 | ||
| 330 | ||
| 331 | void CHttpEventHandler::DumpIt(const TDesC8& aData) | |
| 332 | //Do a formatted dump of binary data | |
| 333 | 	{
 | |
| 334 | // Iterate the supplied block of data in blocks of cols=80 bytes | |
| 335 | const TInt cols=16; | |
| 336 | TInt pos = 0; | |
| 337 | TBuf<KMaxFileName - 2> logLine; | |
| 338 | TBuf<KMaxFileName - 2> anEntry; | |
| 339 | const TInt dataLength = aData.Length(); | |
| 340 | ||
| 341 | while (pos < dataLength) | |
| 342 | 		{
 | |
| 343 | //start-line hexadecimal( a 4 digit number) | |
| 344 | 		anEntry.Format(TRefByValue<const TDesC>_L("%04x : "), pos);
 | |
| 345 | logLine.Append(anEntry); | |
| 346 | ||
| 347 | // Hex output | |
| 348 | TInt offset; | |
| 349 | for (offset = 0; offset < cols; ++offset) | |
| 350 | 			{
 | |
| 351 | if (pos + offset < aData.Length()) | |
| 352 | 				{
 | |
| 353 | TInt nextByte = aData[pos + offset]; | |
| 354 | 				anEntry.Format(TRefByValue<const TDesC>_L("%02x "), nextByte);
 | |
| 355 | logLine.Append(anEntry); | |
| 356 | } | |
| 357 | else | |
| 358 | 				{
 | |
| 359 | //fill the remaining spaces with blanks untill the cols-th Hex number | |
| 360 | 				anEntry.Format(TRefByValue<const TDesC>_L("   "));
 | |
| 361 | logLine.Append(anEntry); | |
| 362 | } | |
| 363 | } | |
| 364 | 			anEntry.Format(TRefByValue<const TDesC>_L(": "));
 | |
| 365 | logLine.Append(anEntry); | |
| 366 | ||
| 367 | // Char output | |
| 368 | for (offset = 0; offset < cols; ++offset) | |
| 369 | 			{
 | |
| 370 | if (pos + offset < aData.Length()) | |
| 371 | 				{
 | |
| 372 | TInt nextByte = aData[pos + offset]; | |
| 373 | if ((nextByte >= ' ') && (nextByte <= '~')) | |
| 374 | 					{
 | |
| 375 | 					anEntry.Format(TRefByValue<const TDesC>_L("%c"), nextByte);
 | |
| 376 | logLine.Append(anEntry); | |
| 377 | } | |
| 378 | else | |
| 379 | 					{
 | |
| 380 | 					anEntry.Format(TRefByValue<const TDesC>_L("."));
 | |
| 381 | logLine.Append(anEntry); | |
| 382 | } | |
| 383 | } | |
| 384 | else | |
| 385 | 				{
 | |
| 386 | 				anEntry.Format(TRefByValue<const TDesC>_L(" "));
 | |
| 387 | logLine.Append(anEntry); | |
| 388 | } | |
| 389 | } | |
| 390 | logLine.Zero(); | |
| 391 | ||
| 392 | // Advance to next byte segment (1 seg= cols) | |
| 393 | pos += cols; | |
| 394 | } | |
| 395 | } | |
| 396 | ||
| 397 | void CHttpEventHandler::SetSilent(TBool aSilent) | |
| 398 | 	{
 | |
| 399 | iSilent = aSilent; | |
| 400 | } | |
| 401 | ||
| 402 | void CHttpEventHandler::CloseSaveFile() | |
| 403 | {
 | |
| 404 | if(iRespBody != NULL) | |
| 405 | 	{		
 | |
| 406 | if(iRespBodyFile.SubSessionHandle() != 0) | |
| 407 | 			{
 | |
| 408 | TInt size; | |
| 409 | iRespBodyFile.Size(size); | |
| 410 | 			DP2("Closing file at size %d, bytes downloaded %d", size, iBytesDownloaded);
 | |
| 411 | iRespBodyFile.Close(); | |
| 412 | } | |
| 413 | } | |
| 414 | } | |
| 415 |