diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 9e066c677a..36fb73c515 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -165,7 +165,7 @@ FA065FFE1286167A00FEA989 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = SOURCE_ROOT; }; FA0660011286168700FEA989 /* Default-Portrait.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait.png"; sourceTree = SOURCE_ROOT; }; FA0660021286168700FEA989 /* Default-Landscape.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape.png"; sourceTree = SOURCE_ROOT; }; - FA2EF9C613630C3B00E3E484 /* libplatform.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform.a; path = libplatform.a; sourceTree = SOURCE_ROOT; }; + FA2EF9C613630C3B00E3E484 /* libplatform.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libplatform.a; sourceTree = SOURCE_ROOT; }; FA34BEC81338D72F00FFB2A7 /* CustomAlertView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CustomAlertView.mm; sourceTree = ""; }; FA34BEC91338D72F00FFB2A7 /* CustomAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomAlertView.h; sourceTree = ""; }; FA4135E1120A263C0062D5B4 /* CountriesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = CountriesViewController.h; path = Settings/CountriesViewController.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; diff --git a/iphone/Maps/Platform/IPhoneDownload.h b/iphone/Maps/Platform/IPhoneDownload.h index f579465437..48cfbd77ac 100644 --- a/iphone/Maps/Platform/IPhoneDownload.h +++ b/iphone/Maps/Platform/IPhoneDownload.h @@ -5,27 +5,21 @@ @interface IPhoneDownload : NSObject { - /// used to delete file on canceling - /// @note DOWNLOADING_FILE_EXTENSION should be appended to get real downloading file name - string m_requestedFileName; + HttpStartParams m_params; + FILE * m_file; /// stores information from the server, can be zero int64_t m_projectedFileSize; - string m_url; NSURLConnection * m_connection; - TDownloadFinishedFunction m_finishObserver; - TDownloadProgressFunction m_progressObserver; - NSInteger m_retryCounter; + + string m_receivedBuffer; } - (void) dealloc; - (std::string const &) Url; -- (BOOL) StartDownloadWithUrl: (char const *)originalUrl andFile: (char const *)file - andFinishFunc: (TDownloadFinishedFunction &)finishFunc - andProgressFunc: (TDownloadProgressFunction &)progressFunc - andUseResume: (BOOL)resume; +- (BOOL) StartDownload: (HttpStartParams const &)params; // Added because release from manager doesn't destroy it immediately... - (void) Cancel; @end diff --git a/iphone/Maps/Platform/IPhoneDownload.mm b/iphone/Maps/Platform/IPhoneDownload.mm index 70f6f2aa79..034370d9a1 100644 --- a/iphone/Maps/Platform/IPhoneDownload.mm +++ b/iphone/Maps/Platform/IPhoneDownload.mm @@ -70,15 +70,13 @@ string GetUniqueHashedId() - (string const &) Url { - return m_url; + return m_params.m_url; } - (void) Cancel { if (m_connection) [m_connection cancel]; - m_progressObserver.clear(); - m_finishObserver.clear(); } - (void) dealloc @@ -93,8 +91,8 @@ string GetUniqueHashedId() if (m_file) { fclose(m_file); - if (!m_requestedFileName.empty()) - remove((m_requestedFileName + DOWNLOADING_FILE_EXTENSION).c_str()); + if (!m_params.m_fileToSave.empty()) + remove((m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str()); } [super dealloc]; } @@ -102,57 +100,79 @@ string GetUniqueHashedId() - (NSMutableURLRequest *) CreateRequest { // Create the request. - NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: [NSString stringWithUTF8String:m_url.c_str()]] + NSMutableURLRequest * request = + [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:m_params.m_url.c_str()]] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:TIMEOUT_IN_SECONDS]; - long long fileSize = ftello(m_file); - if (fileSize > 0) + if (m_file) { - NSLog(@"Resuming download for file %s from position %qi", m_requestedFileName.c_str(), fileSize); - NSString * val = [[NSString alloc] initWithFormat: @"bytes=%qi-", fileSize]; - [request addValue:val forHTTPHeaderField:@"Range"]; - [val release]; + long long fileSize = ftello(m_file); + if (fileSize > 0) + { + NSLog(@"Resuming download for file %s from position %qi", + (m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str(), + fileSize); + NSString * val = [[NSString alloc] initWithFormat: @"bytes=%qi-", fileSize]; + [request addValue:val forHTTPHeaderField:@"Range"]; + [val release]; + } + } + if (!m_params.m_contentType.empty()) + { + [request addValue:[NSString stringWithUTF8String: m_params.m_contentType.c_str()] forHTTPHeaderField:@"Content-Type"]; + } + if (!m_params.m_postData.empty()) + { + NSData * postData = [NSData dataWithBytes:m_params.m_postData.data() length:m_params.m_postData.size()]; + [request setHTTPBody:postData]; } - // send unique id in HTTP user agent header static string const uid = GetUniqueHashedId(); [request addValue:[NSString stringWithUTF8String: uid.c_str()] forHTTPHeaderField:@"User-Agent"]; return request; } -- (BOOL) StartDownloadWithUrl: (char const *)originalUrl andFile: (char const *)file - andFinishFunc: (TDownloadFinishedFunction &)finishFunc andProgressFunc: (TDownloadProgressFunction &)progressFunc - andUseResume: (BOOL)resume +- (BOOL) StartDownload: (HttpStartParams const &)params { - m_finishObserver = finishFunc; - m_progressObserver = progressFunc; + m_params = params; m_retryCounter = 0; - // try to create file first - std::string tmpFile = file; - tmpFile += DOWNLOADING_FILE_EXTENSION; - m_file = fopen(tmpFile.c_str(), resume ? "ab" : "wb"); - if (m_file == 0) + if (!params.m_fileToSave.empty()) { - NSLog(@"Error opening %s file for download: %s", tmpFile.c_str(), strerror(errno)); - // notify observer about error and exit - if (m_finishObserver) - m_finishObserver(originalUrl, EHttpDownloadCantCreateFile); - return NO; + // try to create file first + string const tmpFile = m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION; + m_file = fopen(tmpFile.c_str(), params.m_useResume ? "ab" : "wb"); + if (m_file == 0) + { + NSLog(@"Error opening %s file for download: %s", tmpFile.c_str(), strerror(errno)); + // notify observer about error and exit + if (m_params.m_finish) + { + HttpFinishedParams result; + result.m_url = m_params.m_url; + result.m_file = m_params.m_fileToSave; + result.m_error = EHttpDownloadCantCreateFile; + m_params.m_finish(result); + } + return NO; + } } - m_requestedFileName = file; - m_url = originalUrl; - // create the connection with the request and start loading the data m_connection = [[NSURLConnection alloc] initWithRequest:[self CreateRequest] delegate:self]; if (m_connection == 0) { - NSLog(@"Can't create connection for url %s", originalUrl); + NSLog(@"Can't create connection for url %s", params.m_url.c_str()); // notify observer about error and exit - if (m_finishObserver) - m_finishObserver(originalUrl, EHttpDownloadNoConnectionAvailable); + if (m_params.m_finish) + { + HttpFinishedParams result; + result.m_url = m_params.m_url; + result.m_file = m_params.m_fileToSave; + result.m_error = EHttpDownloadNoConnectionAvailable; + m_params.m_finish(result); + } return NO; } @@ -171,17 +191,26 @@ string GetUniqueHashedId() if (statusCode < 200 || statusCode > 299) { NSLog(@"Received HTTP error code %d, canceling download", statusCode); - long long fileSize = ftello(m_file); - fclose(m_file); - m_file = 0; - // delete file only if it's size is zero to resume download later - if (fileSize == 0) - remove((m_requestedFileName + DOWNLOADING_FILE_EXTENSION).c_str()); + if (m_file) + { + long long fileSize = ftello(m_file); + fclose(m_file); + m_file = 0; + // delete file only if it's size is zero to resume download later + if (fileSize == 0) + remove((m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str()); + } // notify user - if (m_finishObserver) - m_finishObserver(m_url.c_str(), statusCode == 404 ? EHttpDownloadFileNotFound : EHttpDownloadFailed); + if (m_params.m_finish) + { + HttpFinishedParams result; + result.m_url = m_params.m_url; + result.m_file = m_params.m_fileToSave; + result.m_error = statusCode == 404 ? EHttpDownloadFileNotFound : EHttpDownloadFailed; + m_params.m_finish(result); + } // and selfdestruct... - GetDownloadManager().CancelDownload(m_url.c_str()); + GetDownloadManager().CancelDownload(m_params.m_url); return; } } @@ -194,7 +223,7 @@ string GetUniqueHashedId() if (m_projectedFileSize < 0) { fclose(m_file); - m_file = fopen((m_requestedFileName + DOWNLOADING_FILE_EXTENSION).c_str(), "wb"); + m_file = fopen((m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str(), "wb"); } NSLog(@"Projected file size: %qi", m_projectedFileSize); } @@ -202,15 +231,31 @@ string GetUniqueHashedId() - (void) connection: (NSURLConnection *)connection didReceiveData: (NSData *)data { // Append the new data - fwrite([data bytes], 1, [data length], m_file); - if (m_progressObserver) - m_progressObserver(m_url.c_str(), TDownloadProgress(ftello(m_file), m_projectedFileSize)); + int64_t size = -1; + if (m_file) + { + fwrite([data bytes], 1, [data length], m_file); + size = ftello(m_file); + } + else + { + m_receivedBuffer.append(static_cast([data bytes]), [data length]); + size = m_receivedBuffer.size(); + } + if (m_params.m_progress) + { + HttpProgressT progress; + progress.m_url = m_params.m_url; + progress.m_current = size; + progress.m_total = m_projectedFileSize; + m_params.m_progress(progress); + } } - (void) connection: (NSURLConnection *)connection didFailWithError: (NSError *)error { // inform the user - NSLog(@"Connection failed for url %s\n%@", m_url.c_str(), [error localizedDescription]); + NSLog(@"Connection failed for url %s\n%@", m_params.m_url.c_str(), [error localizedDescription]); // retry connection if it's network-specific error if ([error code] < 0 && ++m_retryCounter <= MAX_AUTOMATIC_RETRIES) @@ -229,42 +274,62 @@ string GetUniqueHashedId() // notify observer about error and exit after this if-block } - if (m_finishObserver) - m_finishObserver(m_url.c_str(), EHttpDownloadFailed); + if (m_file) + { + long long fileSize = ftello(m_file); + fclose(m_file); + m_file = 0; + // delete file only if it's size is zero to resume download later + if (fileSize == 0) + remove((m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str()); + } - long long fileSize = ftello(m_file); - fclose(m_file); - m_file = 0; - // delete file only if it's size is zero to resume download later - if (fileSize == 0) - remove((m_requestedFileName + DOWNLOADING_FILE_EXTENSION).c_str()); + if (m_params.m_finish) + { + HttpFinishedParams result; + result.m_url = m_params.m_url; + result.m_file = m_params.m_fileToSave; + result.m_error = EHttpDownloadFailed; + m_params.m_finish(result); + } // and selfdestruct... - GetDownloadManager().CancelDownload(m_url.c_str()); + GetDownloadManager().CancelDownload(m_params.m_url); } - (void) connectionDidFinishLoading: (NSURLConnection *)connection { - // close file - fclose(m_file); - m_file = 0; - // remove temporary extension from downloaded file - remove(m_requestedFileName.c_str()); - bool resultForGUI = true; - if (rename((m_requestedFileName + DOWNLOADING_FILE_EXTENSION).c_str(), m_requestedFileName.c_str())) + bool isLocked = false; + if (m_file) { - resultForGUI = false; - NSLog(@"Can't rename to file %s", m_requestedFileName.c_str()); + // close file + fclose(m_file); + m_file = 0; + // remove temporary extension from downloaded file + remove(m_params.m_fileToSave.c_str()); + if (rename((m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str(), m_params.m_fileToSave.c_str())) + { + isLocked = true; + NSLog(@"Can't rename to file %s", m_params.m_fileToSave.c_str()); + // delete downloaded file + remove((m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str()); + } + else + { + NSLog(@"Successfully downloaded %s", m_params.m_url.c_str()); + } } - else + if (m_params.m_finish) { - NSLog(@"Successfully downloaded %s", m_url.c_str()); + HttpFinishedParams result; + result.m_url = m_params.m_url; + result.m_file = m_params.m_fileToSave; + result.m_data.swap(m_receivedBuffer); + result.m_error = isLocked ? EHttpDownloadFileIsLocked : EHttpDownloadOk; + m_params.m_finish(result); } - - if (m_finishObserver) - m_finishObserver(m_url.c_str(), resultForGUI ? EHttpDownloadOk : EHttpDownloadFileIsLocked); // and selfdestruct... - GetDownloadManager().CancelDownload(m_url.c_str()); + GetDownloadManager().CancelDownload(m_params.m_url); } @end diff --git a/iphone/Maps/Platform/IPhoneDownloadManager.mm b/iphone/Maps/Platform/IPhoneDownloadManager.mm index aa1fd44a08..a1984112b7 100644 --- a/iphone/Maps/Platform/IPhoneDownloadManager.mm +++ b/iphone/Maps/Platform/IPhoneDownloadManager.mm @@ -23,26 +23,21 @@ public: [activeDownloads release]; } - virtual void DownloadFile(char const * url, char const * fileName, - TDownloadFinishedFunction finishFunc, TDownloadProgressFunction progressFunc, bool resume) + virtual void HttpRequest(HttpStartParams const & params) { // check if download is already active for (NSUInteger i = 0; i < [activeDownloads count]; ++i) { IPhoneDownload * download = [activeDownloads objectAtIndex:i]; - if ([download Url] == url) + if ([download Url] == params.m_url) { - NSLog(@"Download is already active for url %s", url); + NSLog(@"Download is already active for url %s", params.m_url.c_str()); return; } } IPhoneDownload * download = [[IPhoneDownload alloc] init]; - if ([download StartDownloadWithUrl:url - andFile:fileName - andFinishFunc:finishFunc - andProgressFunc:progressFunc - andUseResume:resume]) + if ([download StartDownload:params]) { // save download in array to cancel it later if necessary [activeDownloads addObject:download]; @@ -57,7 +52,7 @@ public: } /// @note Doesn't notify clients on canceling! - virtual void CancelDownload(char const * url) + virtual void CancelDownload(string const & url) { // disable network activity indicator in top system toolbar // note that this method is called also from successful/failed download to "selfdestruct" below diff --git a/iphone/Maps/Settings/CountriesViewController.h b/iphone/Maps/Settings/CountriesViewController.h index e14ce3c941..1a71ae4316 100644 --- a/iphone/Maps/Settings/CountriesViewController.h +++ b/iphone/Maps/Settings/CountriesViewController.h @@ -12,6 +12,6 @@ - (id) initWithStorage: (storage::Storage &) storage andIndex: (storage::TIndex const &) index andHeader: (NSString *) header; - (void) OnCountryChange: (storage::TIndex const &) index; -- (void) OnDownload: (storage::TIndex const &) index withProgress: (TDownloadProgress const &) progress; +- (void) OnDownload: (storage::TIndex const &) index withProgress: (HttpProgressT const &) progress; @end diff --git a/iphone/Maps/Settings/CountriesViewController.mm b/iphone/Maps/Settings/CountriesViewController.mm index 399c74cd8d..efe49fb2b0 100644 --- a/iphone/Maps/Settings/CountriesViewController.mm +++ b/iphone/Maps/Settings/CountriesViewController.mm @@ -380,14 +380,15 @@ TIndex g_clickedIndex; } } -- (void) OnDownload: (TIndex const &) index withProgress: (TDownloadProgress const &) progress +- (void) OnDownload: (TIndex const &) index withProgress: (HttpProgressT const &) progress { if (IsOurIndex(index, m_index)) { UITableView * tableView = (UITableView *)self.view; UITableViewCell * cell = [tableView cellForRowAtIndexPath: [NSIndexPath indexPathForRow: RowFromIndex(index) inSection: 0]]; if (cell) - cell.detailTextLabel.text = [NSString stringWithFormat: @"Downloading %qu%%, touch to cancel", progress.first * 100 / progress.second]; + cell.detailTextLabel.text = [NSString stringWithFormat: @"Downloading %qu%%, touch to cancel", + progress.m_current * 100 / progress.m_total]; } } diff --git a/iphone/Maps/Settings/SettingsManager.mm b/iphone/Maps/Settings/SettingsManager.mm index ed3e990aee..34d8f09477 100644 --- a/iphone/Maps/Settings/SettingsManager.mm +++ b/iphone/Maps/Settings/SettingsManager.mm @@ -44,7 +44,7 @@ using namespace storage; } } -- (void) OnCountryDownload: (TIndex const &) index withProgress: (TDownloadProgress const &) progress +- (void) OnCountryDownload: (TIndex const &) index withProgress: (HttpProgressT const &) progress { if (m_navController) { @@ -80,7 +80,7 @@ using namespace storage; SEL changeSel = @selector(OnCountryChange:); TChangeFunc changeImpl = (TChangeFunc)[self methodForSelector:changeSel]; - typedef void (*TProgressFunc)(id, SEL, TIndex const &, TDownloadProgress const &); + typedef void (*TProgressFunc)(id, SEL, TIndex const &, HttpProgressT const &); SEL progressSel = @selector(OnCountryDownload:withProgress:); TProgressFunc progressImpl = (TProgressFunc)[self methodForSelector:progressSel]; diff --git a/platform/download_manager.hpp b/platform/download_manager.hpp index c2f5d4e104..e51c425776 100644 --- a/platform/download_manager.hpp +++ b/platform/download_manager.hpp @@ -2,24 +2,50 @@ #include "../std/stdint.hpp" #include "../std/utility.hpp" - -#include +#include "../std/string.hpp" +#include "../std/function.hpp" /// Appended to all downloading files and removed after successful download #define DOWNLOADING_FILE_EXTENSION ".downloading" -typedef std::pair TDownloadProgress; -typedef boost::function TDownloadProgressFunction; -enum DownloadResult +/// Notifies client about donwload progress +struct HttpProgressT +{ + string m_url; + int64_t m_current; + int64_t m_total; +}; +typedef boost::function HttpProgressCallbackT; + +enum DownloadResultT { EHttpDownloadOk, - EHttpDownloadFileNotFound, // HTTP 404 + EHttpDownloadFileNotFound, //!< HTTP 404 EHttpDownloadFailed, - EHttpDownloadFileIsLocked, // downloaded file can't replace existing locked file - EHttpDownloadCantCreateFile, // file for downloading can't be created + EHttpDownloadFileIsLocked, //!< downloaded file can't replace existing locked file + EHttpDownloadCantCreateFile, //!< file for downloading can't be created EHttpDownloadNoConnectionAvailable }; -typedef boost::function TDownloadFinishedFunction; + +struct HttpFinishedParams +{ + string m_url; + string m_file; //!< if not empty, contains file with retrieved data + string m_data; //!< if not empty, contains received data + DownloadResultT m_error; +}; +typedef boost::function HttpFinishedCallbackT; + +struct HttpStartParams +{ + string m_url; + string m_fileToSave; + HttpFinishedCallbackT m_finish; + HttpProgressCallbackT m_progress; + bool m_useResume; + string m_contentType; + string m_postData; //!< if not empty, send POST instead of GET +}; /// Platform-dependent implementations should derive it /// and implement pure virtual methods @@ -27,11 +53,10 @@ class DownloadManager { public: virtual ~DownloadManager() {} - virtual void DownloadFile(char const * url, char const * fileName, - TDownloadFinishedFunction finish, TDownloadProgressFunction progress, - bool useResume = false) = 0; + + virtual void HttpRequest(HttpStartParams const & params) = 0; /// @note Doesn't notifies clients on canceling! - virtual void CancelDownload(char const * url) = 0; + virtual void CancelDownload(string const & url) = 0; virtual void CancelAllDownloads() = 0; }; diff --git a/platform/platform_tests/download_test.cpp b/platform/platform_tests/download_test.cpp index 4cccaf3ab5..0bda75ce3a 100644 --- a/platform/platform_tests/download_test.cpp +++ b/platform/platform_tests/download_test.cpp @@ -56,36 +56,34 @@ template struct DlObserver { size_t m_downloadsProcessed; - DownloadResult m_result[TMaxDownloadsNum]; - string m_url[TMaxDownloadsNum]; + HttpFinishedParams m_result[TMaxDownloadsNum]; string m_progressUrl; DlObserver() : m_downloadsProcessed(0) { for (size_t i = 0; i < TMaxDownloadsNum; ++i) - m_result[i] = EHttpDownloadFailed; + m_result[i].m_error = EHttpDownloadFailed; } - void OnDownloadFinished(char const * url, DownloadResult result) + void OnDownloadFinished(HttpFinishedParams const & result) { - m_url[m_downloadsProcessed] = url; - m_result[m_downloadsProcessed] = result; - ++m_downloadsProcessed; + m_result[m_downloadsProcessed++] = result; if (m_downloadsProcessed >= TMaxDownloadsNum) STOP_WAITING_FOR_ASYNC_DOWNLOAD; // return control to test function body } - void OnDownloadProgress(char const * url, TDownloadProgress progress) + void OnDownloadProgress(HttpProgressT const & progress) { - m_progressUrl = url; - if (progress.second < 0) + m_progressUrl = progress.m_url; + if (progress.m_total < 0) { - cerr << "Your hosting doesn't support total file size or invalid resume range was given for " << url << endl; + cerr << "Your hosting doesn't support total file size or invalid resume range was given for " + << m_progressUrl << endl; } // for big file - cancel downloading after 40Kb were transferred - if (progress.first > 40 * 1024) - gMgr.CancelDownload(url); + if (progress.m_current > 40 * 1024) + gMgr.CancelDownload(progress.m_url); } }; @@ -95,11 +93,14 @@ UNIT_TEST(SingleDownload) DlObserver observer; FileWriter::DeleteFileX(TEST_FILE_NAME1); - gMgr.DownloadFile(TEST_FILE_URL1, TEST_FILE_NAME1, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); + HttpStartParams params; + params.m_url = TEST_FILE_URL1; + params.m_fileToSave = TEST_FILE_NAME1; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer, _1); + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer.m_result[0], EHttpDownloadOk, ("Do you have internet connection?") ); + TEST_EQUAL( observer.m_result[0].m_error, EHttpDownloadOk, ("Do you have internet connection?") ); TEST( gPlatform.IsFileExists(TEST_FILE_NAME1), () ); FileWriter::DeleteFileX(TEST_FILE_NAME1); } @@ -112,26 +113,29 @@ UNIT_TEST(MultiDownload) FileWriter::DeleteFileX(TEST_FILE_NAME1); FileWriter::DeleteFileX(TEST_FILE_NAME2); FileWriter::DeleteFileX(TEST_FILE_NAME3); - gMgr.DownloadFile(TEST_FILE_URL1, TEST_FILE_NAME1, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); - gMgr.DownloadFile(TEST_FILE_URL2, TEST_FILE_NAME2, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); - gMgr.DownloadFile(TEST_FILE_URL3, TEST_FILE_NAME3, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); + HttpStartParams params; + params.m_url = TEST_FILE_URL1; + params.m_fileToSave = TEST_FILE_NAME1; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer, _1); + gMgr.HttpRequest(params); + params.m_url = TEST_FILE_URL2; + params.m_fileToSave = TEST_FILE_NAME2; + gMgr.HttpRequest(params); + params.m_url = TEST_FILE_URL3; + params.m_fileToSave = TEST_FILE_NAME3; + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer.m_result[0], EHttpDownloadOk, ("Do you have internet connection?") ); + TEST_EQUAL( observer.m_result[0].m_error, EHttpDownloadOk, ("Do you have internet connection?") ); TEST( gPlatform.IsFileExists(TEST_FILE_NAME1), () ); FileWriter::DeleteFileX(TEST_FILE_NAME1); - TEST_EQUAL( observer.m_result[1], EHttpDownloadOk, ("Do you have internet connection?") ); + TEST_EQUAL( observer.m_result[1].m_error, EHttpDownloadOk, ("Do you have internet connection?") ); TEST( gPlatform.IsFileExists(TEST_FILE_NAME2), () ); FileWriter::DeleteFileX(TEST_FILE_NAME2); - TEST_EQUAL( observer.m_result[2], EHttpDownloadOk, ("Do you have internet connection?") ); + TEST_EQUAL( observer.m_result[2].m_error, EHttpDownloadOk, ("Do you have internet connection?") ); TEST( gPlatform.IsFileExists(TEST_FILE_NAME3), () ); FileWriter::DeleteFileX(TEST_FILE_NAME3); } @@ -140,15 +144,18 @@ UNIT_TEST(InvalidUrl) { size_t const NUM = 1; DlObserver observer; - // this should be set to error - observer.m_result[0] = EHttpDownloadOk; + // this should be set to error after executing + observer.m_result[0].m_error = EHttpDownloadOk; - gMgr.DownloadFile(TEST_INVALID_URL, TEST_FILE_NAME1, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); + HttpStartParams params; + params.m_url = TEST_INVALID_URL; + params.m_fileToSave = TEST_FILE_NAME1; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer, _1); + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer.m_result[0], EHttpDownloadFailed, () ); + TEST_EQUAL( observer.m_result[0].m_error, EHttpDownloadFailed, () ); FileWriter::DeleteFileX(TEST_FILE_NAME1); } @@ -183,12 +190,15 @@ UNIT_TEST(DownloadFileExists) TEST_EQUAL(fileContent, str, ("Writer or Reader don't work?..")); } - gMgr.DownloadFile(TEST_FILE_URL1, TEST_FILE_NAME1, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); + HttpStartParams params; + params.m_url = TEST_FILE_URL1; + params.m_fileToSave = TEST_FILE_NAME1; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer, _1); + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer.m_result[0], EHttpDownloadOk, () ); + TEST_EQUAL( observer.m_result[0].m_error, EHttpDownloadOk, () ); { string str; @@ -210,21 +220,26 @@ UNIT_TEST(DownloadResume) f.Write(fileContent.c_str(), fileContent.size()); } - DlObserver observer1; - gMgr.DownloadFile(TEST_FILE_URL1, TEST_FILE_NAME1, - bind(&DlObserver::OnDownloadFinished, &observer1, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer1, _1, _2), - false); + DlObserver observer1; + HttpStartParams params; + params.m_url = TEST_FILE_URL1; + params.m_fileToSave = TEST_FILE_NAME1; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer1, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer1, _1); + params.m_useResume = false; + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer1.m_result[0], EHttpDownloadOk, () ); + TEST_EQUAL( observer1.m_result[0].m_error, EHttpDownloadOk, () ); DlObserver observer2; - gMgr.DownloadFile(TEST_FILE_URL1, TEST_FILE_NAME2, - bind(&DlObserver::OnDownloadFinished, &observer2, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer2, _1, _2), - false); + params.m_url = TEST_FILE_URL1; + params.m_fileToSave = TEST_FILE_NAME2; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer2, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer2, _1); + params.m_useResume = false; + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer2.m_result[0], EHttpDownloadOk, () ); + TEST_EQUAL( observer2.m_result[0].m_error, EHttpDownloadOk, () ); uint64_t size1 = 4, size2 = 5; TEST( GetPlatform().GetFileSize(TEST_FILE_NAME1, size1), ()); @@ -238,12 +253,14 @@ UNIT_TEST(DownloadResume) f.Write(fileContent.c_str(), fileContent.size()); } DlObserver observer3; - gMgr.DownloadFile(TEST_FILE_URL1, TEST_FILE_NAME1, - bind(&DlObserver::OnDownloadFinished, &observer3, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer3, _1, _2), - true); + params.m_url = TEST_FILE_URL1; + params.m_fileToSave = TEST_FILE_NAME1; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer3, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer3, _1); + params.m_useResume = true; + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer3.m_result[0], EHttpDownloadOk, () ); + TEST_EQUAL( observer3.m_result[0].m_error, EHttpDownloadOk, () ); TEST( GetPlatform().GetFileSize(TEST_FILE_NAME1, size1), ()); TEST_EQUAL( size1, size2, () ); @@ -264,12 +281,15 @@ UNIT_TEST(DownloadAbsentFile) size_t const NUM = 1; DlObserver observer; - gMgr.DownloadFile(TEST_ABSENT_FILE_URL, TEST_ABSENT_FILE_NAME, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); + HttpStartParams params; + params.m_url = TEST_ABSENT_FILE_URL; + params.m_fileToSave = TEST_ABSENT_FILE_NAME; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer, _1); + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer.m_result[0], EHttpDownloadFileNotFound, () ); + TEST_EQUAL( observer.m_result[0].m_error, EHttpDownloadFileNotFound, () ); TEST( !GetPlatform().IsFileExists(TEST_ABSENT_FILE_NAME), () ); FileWriter::DeleteFileX(TEST_ABSENT_FILE_NAME); @@ -286,13 +306,46 @@ UNIT_TEST(DownloadLockedFile) FileWriter lockedFile(TEST_LOCKED_FILE_NAME); TEST( GetPlatform().IsFileExists(TEST_LOCKED_FILE_NAME), () ); - gMgr.DownloadFile(TEST_LOCKED_FILE_URL, TEST_LOCKED_FILE_NAME, - bind(&DlObserver::OnDownloadFinished, &observer, _1, _2), - bind(&DlObserver::OnDownloadProgress, &observer, _1, _2)); + HttpStartParams params; + params.m_url = TEST_LOCKED_FILE_URL; + params.m_fileToSave = TEST_LOCKED_FILE_NAME; + params.m_finish = bind(&DlObserver::OnDownloadFinished, &observer, _1); + params.m_progress = bind(&DlObserver::OnDownloadProgress, &observer, _1); + gMgr.HttpRequest(params); WAIT_FOR_ASYNC_DOWNLOAD; - TEST_EQUAL( observer.m_result[0], EHttpDownloadFileIsLocked, () ); + TEST_EQUAL( observer.m_result[0].m_error, EHttpDownloadFileIsLocked, () ); } FileWriter::DeleteFileX(TEST_LOCKED_FILE_NAME); } #endif + +struct HttpPostCallbackHolder +{ + HttpFinishedParams * m_pResult; + HttpPostCallbackHolder() : m_pResult(NULL) {} + ~HttpPostCallbackHolder() { delete m_pResult; } + void OnHttpPost(HttpFinishedParams const & result) + { + m_pResult = new HttpFinishedParams(result); + STOP_WAITING_FOR_ASYNC_DOWNLOAD; + } +}; + +UNIT_TEST(HttpPost) +{ + HttpPostCallbackHolder cbHolder; + HttpStartParams params; + params.m_finish = bind(&HttpPostCallbackHolder::OnHttpPost, &cbHolder, _1); + params.m_contentType = "application/json"; + params.m_postData = "{\"version\":\"1.1.0\",\"request_address\":true}"; + params.m_url = "http://melnichek.ath.cx:34568/unit_tests/post.php"; + gMgr.HttpRequest(params); + WAIT_FOR_ASYNC_DOWNLOAD; + + TEST( cbHolder.m_pResult, () ); + TEST_EQUAL(cbHolder.m_pResult->m_error, EHttpDownloadOk, + ("HTTP POST failed with error", cbHolder.m_pResult->m_error)); + TEST_EQUAL(cbHolder.m_pResult->m_data, "HTTP POST is OK", + ("Server sent invalid POST reply")); +} diff --git a/platform/qt_download.cpp b/platform/qt_download.cpp index aa31260c9d..dce170210c 100644 --- a/platform/qt_download.cpp +++ b/platform/qt_download.cpp @@ -91,36 +91,35 @@ static QString UserAgent() return userAgent; } -QtDownload::QtDownload(QtDownloadManager & manager, char const * url, - char const * fileName, TDownloadFinishedFunction & finish, - TDownloadProgressFunction & progress, bool useResume) - : QObject(&manager), m_currentUrl(url), m_reply(0), m_file(0), - m_httpRequestAborted(false), m_finish(finish), m_progress(progress), - m_retryCounter(0) +QtDownload::QtDownload(QtDownloadManager & manager, HttpStartParams const & params) + : QObject(&manager), m_currentUrl(params.m_url.c_str()), m_reply(0), + m_retryCounter(0), m_params(params) { - // use temporary file for download - QString tmpFileName(fileName); - tmpFileName += DOWNLOADING_FILE_EXTENSION; - - m_file = new QFile(tmpFileName); - if (!m_file->open(useResume ? QIODevice::Append : QIODevice::WriteOnly)) + // if we need to save server response to the file... + if (!m_params.m_fileToSave.empty()) { - QString const err = m_file->errorString(); - LOG(LERROR, ("Can't open file while downloading", qPrintable(tmpFileName), qPrintable(err))); - delete m_file; - m_file = 0; - - if (m_finish) - m_finish(url, EHttpDownloadCantCreateFile); - // mark itself to delete - deleteLater(); - return; + // use temporary file for download + m_file.setFileName((m_params.m_fileToSave + DOWNLOADING_FILE_EXTENSION).c_str()); + if (!m_file.open(m_params.m_useResume ? QIODevice::Append : QIODevice::WriteOnly)) + { + QString const err = m_file.errorString(); + LOG(LERROR, ("Can't open file while downloading", qPrintable(m_file.fileName()), qPrintable(err))); + if (m_params.m_finish) + { + HttpFinishedParams result; + result.m_error = EHttpDownloadCantCreateFile; + result.m_file = m_params.m_fileToSave; + result.m_url = m_params.m_url; + m_params.m_finish(result); + } + // mark itself to delete + deleteLater(); + return; + } } - // url acts as a key to find this download by QtDownloadManager::findChild(url) - setObjectName(url); - // this can be redirected later - m_currentUrl = url; + setObjectName(m_params.m_url.c_str()); + StartRequest(); } @@ -128,20 +127,21 @@ QtDownload::~QtDownload() { if (m_reply) { - m_httpRequestAborted = true; - // calls OnHttpFinished + // don't notify client when aborted + disconnect(m_reply, SIGNAL(finished()), this, SLOT(OnHttpFinished())); + disconnect(m_reply, SIGNAL(readyRead()), this, SLOT(OnHttpReadyRead())); + disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), + this, SLOT(OnUpdateDataReadProgress(qint64, qint64))); m_reply->abort(); - } - LOG(LDEBUG, (qPrintable(objectName()))); -} + delete m_reply; -void QtDownload::StartDownload(QtDownloadManager & manager, char const * url, - char const * fileName, TDownloadFinishedFunction & finish, - TDownloadProgressFunction & progress, bool useResume) -{ - ASSERT(url && fileName, ()); - // manager is responsible for auto deleting - new QtDownload(manager, url, fileName, finish, progress, useResume); + if (m_file.isOpen()) + { + m_file.close(); + m_file.remove(); + } + } + LOG(LDEBUG, (m_params.m_url)); } void QtDownload::StartRequest() @@ -149,39 +149,42 @@ void QtDownload::StartRequest() QNetworkRequest httpRequest(m_currentUrl); // set user-agent with unique client id httpRequest.setRawHeader("User-Agent", UserAgent().toAscii()); - qint64 fileSize = m_file->size(); - if (fileSize > 0) // need resume - httpRequest.setRawHeader("Range", QString("bytes=%1-").arg(fileSize).toAscii()); - m_reply = static_cast(parent())->NetAccessManager().get(httpRequest); + if (m_file.isOpen()) + { + qint64 const fileSize = m_file.size(); + if (fileSize > 0) // need resume + httpRequest.setRawHeader("Range", QString("bytes=%1-").arg(fileSize).toAscii()); + } + if (!m_params.m_contentType.empty()) + httpRequest.setHeader(QNetworkRequest::ContentTypeHeader, m_params.m_contentType.c_str()); + if (m_params.m_postData.empty()) + m_reply = static_cast(parent())->NetAccessManager().get(httpRequest); + else + { + m_reply = static_cast(parent())->NetAccessManager().post( + httpRequest, m_params.m_postData.c_str()); + } connect(m_reply, SIGNAL(finished()), this, SLOT(OnHttpFinished())); connect(m_reply, SIGNAL(readyRead()), this, SLOT(OnHttpReadyRead())); connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(OnUpdateDataReadProgress(qint64, qint64))); } +static DownloadResultT TranslateError(QNetworkReply::NetworkError err) +{ + switch (err) + { + case QNetworkReply::NoError: return EHttpDownloadOk; + case QNetworkReply::ContentNotFoundError: return EHttpDownloadFileNotFound; + case QNetworkReply::HostNotFoundError: return EHttpDownloadFailed; + default: return EHttpDownloadFailed; + } +} + void QtDownload::OnHttpFinished() { - if (m_httpRequestAborted) - { // we're called from destructor - m_file->close(); - m_file->remove(); - delete m_file; - m_file = 0; - - m_reply->deleteLater(); - m_reply = 0; - - // don't notify client when aborted - //OnDownloadFinished(ToUtf8(m_originalUrl).c_str(), false, "Download was aborted"); - - return; - } - - m_file->flush(); - m_file->close(); - - QVariant redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); - QNetworkReply::NetworkError netError = m_reply->error(); + QVariant const redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + QNetworkReply::NetworkError const netError = m_reply->error(); if (netError) { if (netError <= QNetworkReply::UnknownNetworkError && ++m_retryCounter <= MAX_AUTOMATIC_RETRIES) @@ -191,10 +194,12 @@ void QtDownload::OnHttpFinished() return; } // do not delete file if we can resume it's downloading later - if (m_file->pos() == 0) - m_file->remove(); - delete m_file; - m_file = 0; + if (m_file.isOpen()) + { + m_file.close(); + if (m_file.size() == 0) + m_file.remove(); + } QString const err = m_reply->errorString(); LOG(LWARNING, ("Download failed", qPrintable(err))); @@ -202,9 +207,14 @@ void QtDownload::OnHttpFinished() m_reply->deleteLater(); m_reply = 0; - if (m_finish) - m_finish(objectName().toUtf8().data(), netError == QNetworkReply::ContentNotFoundError - ? EHttpDownloadFileNotFound : EHttpDownloadFailed); + if (m_params.m_finish) + { + HttpFinishedParams result; + result.m_file = m_params.m_fileToSave; + result.m_url = m_params.m_url; + result.m_error = TranslateError(netError); + m_params.m_finish(result); + } // selfdestruct deleteLater(); } @@ -212,35 +222,48 @@ void QtDownload::OnHttpFinished() { m_currentUrl = m_currentUrl.resolved(redirectionTarget.toUrl()); LOG(LINFO, ("HTTP redirect", m_currentUrl.toEncoded().data())); - m_file->open(QIODevice::WriteOnly); - m_file->resize(0); + if (m_file.isOpen()) + { + m_file.close(); + m_file.open(QIODevice::WriteOnly); + m_file.resize(0); + } m_reply->deleteLater(); + m_reply = 0; StartRequest(); return; } else { // download succeeded - // original file name which was requested to download - QString const originalFileName = m_file->fileName().left(m_file->fileName().lastIndexOf(DOWNLOADING_FILE_EXTENSION)); - bool resultForGui = true; - // delete original file if it exists - QFile::remove(originalFileName); - if (!m_file->rename(originalFileName)) - { // sh*t... file is locked and can't be removed - m_file->remove(); - // report error to GUI - LOG(LWARNING, ("File exists and can't be replaced by downloaded one:", qPrintable(originalFileName))); - resultForGui = false; + bool fileIsLocked = false; + if (m_file.isOpen()) + { + m_file.close(); + // delete original file if it exists + QFile::remove(m_params.m_fileToSave.c_str()); + if (!m_file.rename(m_params.m_fileToSave.c_str())) + { // sh*t... file is locked and can't be removed + m_file.remove(); + // report error to GUI + LOG(LWARNING, ("File exists and can't be replaced by downloaded one:", m_params.m_fileToSave)); + fileIsLocked = true; + } + } + + if (m_params.m_finish) + { + HttpFinishedParams result; + result.m_url = m_params.m_url; + result.m_file = m_params.m_fileToSave; + QByteArray data = m_reply->readAll(); + result.m_data.assign(data.constData(), data.size()); + result.m_error = fileIsLocked ? EHttpDownloadFileIsLocked : EHttpDownloadOk; + m_params.m_finish(result); } - delete m_file; - m_file = 0; m_reply->deleteLater(); m_reply = 0; - - if (m_finish) - m_finish(qPrintable(objectName()), resultForGui ? EHttpDownloadOk : EHttpDownloadFileIsLocked); // selfdestruct deleteLater(); } @@ -252,12 +275,21 @@ void QtDownload::OnHttpReadyRead() // We read all of its new data and write it into the file. // That way we use less RAM than when reading it at the finished() // signal of the QNetworkReply - if (m_file && m_reply) - m_file->write(m_reply->readAll()); + if (m_file.isOpen() && m_reply) + m_file.write(m_reply->readAll()); + + // @note that for requests where m_file is not used, data accumulates + // and m_reply->readAll() should be called in finish() slot handler } void QtDownload::OnUpdateDataReadProgress(qint64 bytesRead, qint64 totalBytes) { - if (!m_httpRequestAborted && m_progress) - m_progress(qPrintable(objectName()), TDownloadProgress(bytesRead, totalBytes)); + if (m_params.m_progress) + { + HttpProgressT p; + p.m_current = bytesRead; + p.m_total = totalBytes; + p.m_url = m_params.m_url; + m_params.m_progress(p); + } } diff --git a/platform/qt_download.hpp b/platform/qt_download.hpp index 5d44ce7926..b50ff7e214 100644 --- a/platform/qt_download.hpp +++ b/platform/qt_download.hpp @@ -11,17 +11,14 @@ class QtDownload : public QObject private: Q_OBJECT + // can be changed during server redirections QUrl m_currentUrl; QNetworkReply * m_reply; - QFile * m_file; - bool m_httpRequestAborted; - TDownloadFinishedFunction m_finish; - TDownloadProgressFunction m_progress; + QFile m_file; int m_retryCounter; - QtDownload(QtDownloadManager & manager, char const * url, char const * fileName, - TDownloadFinishedFunction & finish, TDownloadProgressFunction & progress, - bool useResume); + HttpStartParams m_params; + void StartRequest(); private slots: @@ -32,7 +29,6 @@ private slots: public: /// Created instance is automanaged as a manager's child /// and can be manipulated later by manager->findChild(url); - static void StartDownload(QtDownloadManager & manager, char const * url, char const * fileName, - TDownloadFinishedFunction & finish, TDownloadProgressFunction & progress, bool useResume); + QtDownload(QtDownloadManager & manager, HttpStartParams const & params); virtual ~QtDownload(); }; diff --git a/platform/qt_download_manager.cpp b/platform/qt_download_manager.cpp index 6635096646..11ee905e16 100644 --- a/platform/qt_download_manager.cpp +++ b/platform/qt_download_manager.cpp @@ -3,22 +3,24 @@ #include "../base/assert.hpp" -void QtDownloadManager::DownloadFile(char const * url, char const * fileName, - TDownloadFinishedFunction finish, TDownloadProgressFunction progress, - bool useResume) +void QtDownloadManager::HttpRequest(HttpStartParams const & params) { - QList downloads = findChildren(url); + ASSERT(!params.m_url.empty(), ()); + QList downloads = findChildren(params.m_url.c_str()); if (downloads.empty()) - QtDownload::StartDownload(*this, url, fileName, finish, progress, useResume); + { + // manager is responsible for auto deleting + new QtDownload(*this, params); + } else { ASSERT(false, ("Download with the same url is already in progress!")); } } -void QtDownloadManager::CancelDownload(char const * url) +void QtDownloadManager::CancelDownload(string const & url) { - QList downloads = findChildren(url); + QList downloads = findChildren(url.c_str()); while (!downloads.isEmpty()) delete downloads.takeFirst(); } diff --git a/platform/qt_download_manager.hpp b/platform/qt_download_manager.hpp index b58b6ef4c6..27a581de98 100644 --- a/platform/qt_download_manager.hpp +++ b/platform/qt_download_manager.hpp @@ -9,10 +9,8 @@ class QtDownloadManager : public QObject, public DownloadManager QNetworkAccessManager m_netAccessManager; public: - virtual void DownloadFile(char const * url, char const * fileName, - TDownloadFinishedFunction finish, TDownloadProgressFunction progress, - bool useResume); - virtual void CancelDownload(char const * url); + virtual void HttpRequest(HttpStartParams const & params); + virtual void CancelDownload(string const & url); virtual void CancelAllDownloads(); QNetworkAccessManager & NetAccessManager() { return m_netAccessManager; } diff --git a/qt/update_dialog.cpp b/qt/update_dialog.cpp index 529c8747c1..53d987e70a 100644 --- a/qt/update_dialog.cpp +++ b/qt/update_dialog.cpp @@ -375,11 +375,11 @@ namespace qt } void UpdateDialog::OnCountryDownloadProgress(TIndex const & index, - TDownloadProgress const & progress) + HttpProgressT const & progress) { QTreeWidgetItem * item = GetTreeItemByIndex(*m_tree, index); if (item) - item->setText(KColumnIndexSize, QString("%1%").arg(progress.first * 100 / progress.second)); + item->setText(KColumnIndexSize, QString("%1%").arg(progress.m_current * 100 / progress.m_total)); } void UpdateDialog::ShowDialog() diff --git a/qt/update_dialog.hpp b/qt/update_dialog.hpp index dcfc3ddecf..fab22f6b5e 100644 --- a/qt/update_dialog.hpp +++ b/qt/update_dialog.hpp @@ -23,7 +23,7 @@ namespace qt //@{ void OnCountryChanged(storage::TIndex const & index); void OnCountryDownloadProgress(storage::TIndex const & index, - TDownloadProgress const & progress); + HttpProgressT const & progress); void OnUpdateRequest(storage::TUpdateResult result, string const & description); //@} diff --git a/storage/storage.cpp b/storage/storage.cpp index 63e7e6694c..b0a45d07e8 100644 --- a/storage/storage.cpp +++ b/storage/storage.cpp @@ -15,16 +15,13 @@ #include "../std/set.hpp" #include "../std/algorithm.hpp" #include "../std/target_os.hpp" - -#include - -#include "../base/start_mem_debug.hpp" +#include "../std/bind.hpp" namespace storage { const int TIndex::INVALID = -1; - static string ErrorString(DownloadResult res) + static string ErrorString(DownloadResultT res) { switch (res) { @@ -168,8 +165,9 @@ namespace storage if (m_queue.size() == 1) { // reset total country's download progress - TLocalAndRemoteSize size = CountryByIndex(index).Size(); - m_countryProgress = TDownloadProgress(0, size.second); + TLocalAndRemoteSize const size = CountryByIndex(index).Size(); + m_countryProgress.m_current = 0; + m_countryProgress.m_total = size.second; DownloadNextCountryFromQueue(); } @@ -207,12 +205,13 @@ namespace storage { if (!IsTileDownloaded(*it)) { - GetDownloadManager().DownloadFile( - (UpdateBaseUrl() + UrlEncode(it->first)).c_str(), - (GetPlatform().WritablePathForFile(it->first).c_str()), - bind(&Storage::OnMapDownloadFinished, this, _1, _2), - bind(&Storage::OnMapDownloadProgress, this, _1, _2), - true); // enabled resume support by default + HttpStartParams params; + params.m_url = UpdateBaseUrl() + UrlEncode(it->first); + params.m_fileToSave = GetPlatform().WritablePathForFile(it->first); + params.m_finish = bind(&Storage::OnMapDownloadFinished, this, _1); + params.m_progress = bind(&Storage::OnMapDownloadProgress, this, _1); + params.m_useResume = true; // enabled resume support by default + GetDownloadManager().HttpRequest(params); // notify GUI - new status for country, "Downloading" if (m_observerChange) m_observerChange(index); @@ -223,7 +222,10 @@ namespace storage m_queue.pop_front(); // reset total country's download progress if (!m_queue.empty()) - m_countryProgress = TDownloadProgress(0, CountryByIndex(m_queue.front()).Size().second); + { + m_countryProgress.m_current = 0; + m_countryProgress.m_total = CountryByIndex(m_queue.front()).Size().second; + } // and notify GUI - new status for country, "OnDisk" if (m_observerChange) m_observerChange(index); @@ -346,20 +348,15 @@ namespace storage m_observerUpdateRequest.clear(); } - string FileFromUrl(string const & url) - { - return UrlDecode(url.substr(url.find_last_of('/') + 1, string::npos)); - } - - void Storage::OnMapDownloadFinished(char const * url, DownloadResult result) + void Storage::OnMapDownloadFinished(HttpFinishedParams const & result) { if (m_queue.empty()) { - ASSERT(false, ("Invalid url?", url)); + ASSERT(false, ("Invalid url?", result.m_url)); return; } - if (result != EHttpDownloadOk) + if (result.m_error != EHttpDownloadOk) { // remove failed country from the queue TIndex failedIndex = m_queue.front(); @@ -373,19 +370,18 @@ namespace storage { TLocalAndRemoteSize size = CountryByIndex(m_queue.front()).Size(); if (size.second != 0) - m_countryProgress.first = size.first; + m_countryProgress.m_current = size.first; // activate downloaded map piece - string const datFile = GetPlatform().ReadPathForFile(FileFromUrl(url)); - m_addMap(datFile); + m_addMap(result.m_file); feature::DataHeader header; - header.Load(FilesContainerR(datFile).GetReader(HEADER_FILE_TAG)); + header.Load(FilesContainerR(result.m_file).GetReader(HEADER_FILE_TAG)); m_updateRect(header.GetBounds()); } DownloadNextCountryFromQueue(); } - void Storage::OnMapDownloadProgress(char const * /*url*/, TDownloadProgress progress) + void Storage::OnMapDownloadProgress(HttpProgressT const & progress) { if (m_queue.empty()) { @@ -394,29 +390,34 @@ namespace storage } if (m_observerProgress) - m_observerProgress(m_queue.front(), - TDownloadProgress(m_countryProgress.first + progress.first, m_countryProgress.second)); + { + HttpProgressT p(progress); + p.m_current = m_countryProgress.m_current + progress.m_current; + p.m_total = m_countryProgress.m_total; + m_observerProgress(m_queue.front(), p); + } } void Storage::CheckForUpdate() { // at this moment we support only binary update checks string const update = UpdateBaseUrl() + BINARY_UPDATE_FILE/*DATA_UPDATE_FILE*/; - GetDownloadManager().CancelDownload(update.c_str()); - GetDownloadManager().DownloadFile( - update.c_str(), - (GetPlatform().WritablePathForFile(DATA_UPDATE_FILE)).c_str(), - bind(&Storage::OnBinaryUpdateCheckFinished, this, _1, _2), - TDownloadProgressFunction(), false); + GetDownloadManager().CancelDownload(update); + HttpStartParams params; + params.m_url = update; + params.m_fileToSave = GetPlatform().WritablePathForFile(DATA_UPDATE_FILE); + params.m_finish = bind(&Storage::OnBinaryUpdateCheckFinished, this, _1); + params.m_useResume = false; + GetDownloadManager().HttpRequest(params); } - void Storage::OnDataUpdateCheckFinished(char const * url, DownloadResult result) + void Storage::OnDataUpdateCheckFinished(HttpFinishedParams const & params) { - if (result != EHttpDownloadOk) + if (params.m_error != EHttpDownloadOk) { - LOG(LWARNING, ("Update check failed for url:", url)); + LOG(LWARNING, ("Update check failed for url:", params.m_url)); if (m_observerUpdateRequest) - m_observerUpdateRequest(EDataCheckFailed, ErrorString(result)); + m_observerUpdateRequest(EDataCheckFailed, ErrorString(params.m_error)); } else { // @TODO parse update file and notify GUI @@ -442,20 +443,20 @@ namespace storage // LOG(LINFO, ("Update check complete")); } - void Storage::OnBinaryUpdateCheckFinished(char const * url, DownloadResult result) + void Storage::OnBinaryUpdateCheckFinished(HttpFinishedParams const & params) { - if (result == EHttpDownloadFileNotFound) + if (params.m_error == EHttpDownloadFileNotFound) { // no binary update is available if (m_observerUpdateRequest) m_observerUpdateRequest(ENoAnyUpdateAvailable, "No update is available"); } - else if (result == EHttpDownloadOk) + else if (params.m_error == EHttpDownloadOk) { // update is available! try { if (m_observerUpdateRequest) { - string const updateTextFilePath = GetPlatform().ReadPathForFile(FileFromUrl(url)); + string const updateTextFilePath = GetPlatform().ReadPathForFile(params.m_file); FileReader file(updateTextFilePath); m_observerUpdateRequest(ENewBinaryAvailable, file.ReadAsText()); } @@ -470,7 +471,7 @@ namespace storage else { // connection error if (m_observerUpdateRequest) - m_observerUpdateRequest(EBinaryCheckFailed, ErrorString(result)); + m_observerUpdateRequest(EBinaryCheckFailed, ErrorString(params.m_error)); } } } diff --git a/storage/storage.hpp b/storage/storage.hpp index a4ab27f8f8..38e41fd60c 100644 --- a/storage/storage.hpp +++ b/storage/storage.hpp @@ -71,7 +71,7 @@ namespace storage typedef list TQueue; TQueue m_queue; /// used to correctly calculate total country download progress - TDownloadProgress m_countryProgress; + HttpProgressT m_countryProgress; typedef set TFailedCountries; /// stores countries which download has failed recently @@ -80,7 +80,7 @@ namespace storage /// @name Communicate with GUI //@{ typedef boost::function TObserverChangeCountryFunction; - typedef boost::function TObserverProgressFunction; + typedef boost::function TObserverProgressFunction; typedef boost::function TUpdateRequestFunction; TObserverChangeCountryFunction m_observerChange; TObserverProgressFunction m_observerProgress; @@ -114,10 +114,10 @@ namespace storage /// @name Called from DownloadManager //@{ - void OnMapDownloadFinished(char const * url, DownloadResult result); - void OnMapDownloadProgress(char const * url, TDownloadProgress progress); - void OnDataUpdateCheckFinished(char const * url, DownloadResult result); - void OnBinaryUpdateCheckFinished(char const * url, DownloadResult result); + void OnMapDownloadFinished(HttpFinishedParams const & params); + void OnMapDownloadProgress(HttpProgressT const & progress); + void OnDataUpdateCheckFinished(HttpFinishedParams const & params); + void OnBinaryUpdateCheckFinished(HttpFinishedParams const & params); //@} /// @name Current impl supports only one observer