1 // HTTPRepository.cxx -- plain HTTP TerraSync remote client
3 // Copyright (C) 20126 James Turner <zakalawe@mac.com>
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "HTTPRepository.hxx"
21 #include <simgear_config.h>
36 #include "simgear/debug/logstream.hxx"
37 #include "simgear/misc/strutils.hxx"
38 #include <simgear/misc/sg_dir.hxx>
39 #include <simgear/io/HTTPClient.hxx>
40 #include <simgear/io/sg_file.hxx>
41 #include <simgear/misc/sgstream.hxx>
42 #include <simgear/structure/exception.hxx>
43 #include <simgear/timing/timestamp.hxx>
45 #include <simgear/misc/sg_hash.hxx>
47 #if defined(SG_WINDOWS)
50 * public domain strtok_r() by Charlie Gordon
52 * from comp.lang.c 9/14/2007
54 * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
56 * (Declaration that it's public domain):
57 * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
72 str += strspn(str, delim);
81 str += strcspn(str, delim);
99 class HTTPRepoGetRequest : public HTTP::Request
102 HTTPRepoGetRequest(HTTPDirectory* d, const std::string& u) :
108 virtual void cancel();
110 size_t contentSize() const
115 void setContentSize(size_t sz)
120 HTTPDirectory* _directory;
124 typedef SGSharedPtr<HTTPRepoGetRequest> RepoRequestPtr;
126 class HTTPRepoPrivate
129 struct HashCacheEntry
131 std::string filePath;
138 typedef std::vector<HashCacheEntry> HashCache;
145 AbstractRepository::ResultCode error;
148 typedef std::vector<Failure> FailureList;
149 FailureList failures;
151 HTTPRepoPrivate(HTTPRepository* parent) :
152 hashCacheDirty(false),
155 status(AbstractRepository::REPO_NO_ERROR),
161 HTTPRepository* p; // link back to outer
166 AbstractRepository::ResultCode status;
167 HTTPDirectory* rootDir;
168 size_t totalDownloaded;
170 HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name,
172 HTTP::Request_ptr updateDir(HTTPDirectory* dir, const std::string& hash,
175 std::string hashForPath(const SGPath& p);
176 void updatedFileContents(const SGPath& p, const std::string& newHash);
177 void parseHashCache();
178 std::string computeHashForPath(const SGPath& p);
179 void writeHashCache();
181 void failedToGetRootIndex(AbstractRepository::ResultCode st);
182 void failedToUpdateChild(const SGPath& relativePath,
183 AbstractRepository::ResultCode fileStatus);
185 typedef std::vector<RepoRequestPtr> RequestVector;
186 RequestVector queuedRequests,
189 void makeRequest(RepoRequestPtr req);
190 void finishedRequest(const RepoRequestPtr& req);
192 HTTPDirectory* getOrCreateDirectory(const std::string& path);
193 bool deleteDirectory(const std::string& path);
195 typedef std::vector<HTTPDirectory*> DirectoryVector;
196 DirectoryVector directories;
210 ChildInfo(Type ty, const char* nameData, const char* hashData) :
213 hash(hashData ? hashData : ""),
218 ChildInfo(const ChildInfo& other) :
222 sizeInBytes(other.sizeInBytes)
225 void setSize(const char* sizeData)
227 sizeInBytes = ::strtol(sizeData, NULL, 10);
230 bool operator<(const ChildInfo& other) const
232 return name < other.name;
236 std::string name, hash;
240 typedef std::vector<ChildInfo> ChildInfoList;
241 ChildInfoList children;
244 HTTPDirectory(HTTPRepoPrivate* repo, const std::string& path) :
250 SGPath p(absolutePath());
253 // already exists on disk
254 parseDirIndex(children);
255 std::sort(children.begin(), children.end());
256 } catch (sg_exception& ) {
257 // parsing cache failed
263 HTTPRepoPrivate* repository() const
268 std::string url() const
270 if (_relativePath.str().empty()) {
271 return _repository->baseUrl;
274 return _repository->baseUrl + "/" + _relativePath.str();
277 void dirIndexUpdated(const std::string& hash)
279 SGPath fpath(_relativePath);
280 fpath.append(".dirindex");
281 _repository->updatedFileContents(fpath, hash);
284 parseDirIndex(children);
285 std::sort(children.begin(), children.end());
288 void failedToUpdate(AbstractRepository::ResultCode status)
290 if (_relativePath.isNull()) {
292 _repository->failedToGetRootIndex(status);
294 _repository->failedToUpdateChild(_relativePath, status);
298 void updateChildrenBasedOnHash()
300 //SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
302 string_list indexNames = indexChildren(),
303 toBeUpdated, orphans;
304 simgear::Dir d(absolutePath());
305 PathList fsChildren = d.children(0);
306 PathList::const_iterator it = fsChildren.begin();
308 for (; it != fsChildren.end(); ++it) {
309 SG_LOG(SG_TERRASYNC, SG_DEBUG, "processing child: '" << it->str() << "', isDir=" << it->isDir() );
310 ChildInfo info(it->isDir() ? ChildInfo::DirectoryType : ChildInfo::FileType,
311 it->file().c_str(), NULL);
312 std::string hash = hashForChild(info);
313 SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash is: '" << hash << "'" );
315 ChildInfoList::iterator c = findIndexChild(it->file());
316 if (c == children.end()) {
317 SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" );
318 orphans.push_back(it->file());
319 } else if (c->hash != hash) {
320 SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() << "', c->name=" << c->name );
321 // file exists, but hash mismatch, schedule update
323 SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists but hash is wrong for:" << c->name);
324 SG_LOG(SG_TERRASYNC, SG_DEBUG, "on disk:" << hash << " vs in info:" << c->hash);
327 toBeUpdated.push_back(c->name);
329 // file exists and hash is valid. If it's a directory,
330 // perform a recursive check.
331 SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists hash is good:" << c->name);
332 if (c->type == ChildInfo::DirectoryType) {
333 SG_LOG(SG_TERRASYNC, SG_DEBUG, "going recursive for:" << c->name);
334 SGPath p(relativePath());
336 HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
337 childDir->updateChildrenBasedOnHash();
341 // remove existing file system children from the index list,
342 // so we can detect new children
343 SG_LOG(SG_TERRASYNC, SG_DEBUG, "looking for name in indexNames:" << c->name);
344 string_list::iterator it = std::find(indexNames.begin(), indexNames.end(), c->name);
345 if (it != indexNames.end()) {
346 SG_LOG(SG_TERRASYNC, SG_DEBUG, "found name in indexNames, erasing:" << c->name);
347 indexNames.erase(it);
349 } // of real children iteration
351 // all remaining names in indexChilden are new children
352 toBeUpdated.insert(toBeUpdated.end(), indexNames.begin(), indexNames.end());
354 removeOrphans(orphans);
355 scheduleUpdates(toBeUpdated);
358 void removeOrphans(const string_list& orphans)
360 string_list::const_iterator it;
361 for (it = orphans.begin(); it != orphans.end(); ++it) {
366 string_list indexChildren() const
369 r.reserve(children.size());
370 ChildInfoList::const_iterator it;
371 for (it=children.begin(); it != children.end(); ++it) {
372 r.push_back(it->name);
377 void scheduleUpdates(const string_list& names)
379 string_list::const_iterator it;
380 for (it = names.begin(); it != names.end(); ++it) {
381 ChildInfoList::iterator cit = findIndexChild(*it);
382 if (cit == children.end()) {
383 SG_LOG(SG_TERRASYNC, SG_WARN, "scheduleUpdate, unknown child:" << *it);
387 if (cit->type == ChildInfo::FileType) {
388 _repository->updateFile(this, *it, cit->sizeInBytes);
390 SGPath p(relativePath());
392 HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
393 _repository->updateDir(childDir, cit->hash, cit->sizeInBytes);
398 SGPath absolutePath() const
400 SGPath r(_repository->basePath);
401 r.append(_relativePath.str());
405 SGPath relativePath() const
407 return _relativePath;
410 void didUpdateFile(const std::string& file, const std::string& hash, size_t sz)
412 // check hash matches what we expected
413 ChildInfoList::iterator it = findIndexChild(file);
414 if (it == children.end()) {
415 SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
417 SGPath fpath(_relativePath);
420 if (it->hash != hash) {
421 _repository->failedToUpdateChild(_relativePath, AbstractRepository::REPO_ERROR_CHECKSUM);
423 _repository->updatedFileContents(fpath, hash);
424 _repository->totalDownloaded += sz;
425 //SG_LOG(SG_TERRASYNC, SG_INFO, "did update:" << fpath);
427 } // of found in child list
430 void didFailToUpdateFile(const std::string& file,
431 AbstractRepository::ResultCode status)
433 SGPath fpath(_relativePath);
435 _repository->failedToUpdateChild(fpath, status);
441 ChildWithName(const std::string& n) : name(n) {}
444 bool operator()(const ChildInfo& info) const
445 { return info.name == name; }
448 ChildInfoList::iterator findIndexChild(const std::string& name)
450 return std::find_if(children.begin(), children.end(), ChildWithName(name));
453 bool parseDirIndex(ChildInfoList& children)
455 SGPath p(absolutePath());
456 p.append(".dirindex");
461 std::ifstream indexStream( p.c_str(), std::ios::in );
463 if ( !indexStream.is_open() ) {
464 throw sg_io_exception("cannot open dirIndex file", p);
467 char lineBuffer[512];
470 while (!indexStream.eof() ) {
471 indexStream.getline(lineBuffer, 512);
473 char* typeData = ::strtok_r(lineBuffer, ":", &lastToken);
475 continue; // skip blank line
480 throw sg_io_exception("Malformed dir index file", p);
483 if (!strcmp(typeData, "version")) {
485 } else if (!strcmp(typeData, "path")) {
489 char* nameData = ::strtok_r(NULL, ":", &lastToken);
490 char* hashData = ::strtok_r(NULL, ":", &lastToken);
491 char* sizeData = ::strtok_r(NULL, ":", &lastToken);
493 if (typeData[0] == 'f') {
494 children.push_back(ChildInfo(ChildInfo::FileType, nameData, hashData));
495 } else if (typeData[0] == 'd') {
496 children.push_back(ChildInfo(ChildInfo::DirectoryType, nameData, hashData));
498 throw sg_io_exception("Malformed line code in dir index file", p);
502 children.back().setSize(sizeData);
509 void removeChild(const std::string& name)
511 SGPath p(absolutePath());
515 SGPath fpath(_relativePath);
519 ok = _repository->deleteDirectory(fpath.str());
521 // remove the hash cache entry
522 _repository->updatedFileContents(fpath, std::string());
527 SG_LOG(SG_TERRASYNC, SG_WARN, "removal failed for:" << p);
528 throw sg_io_exception("Failed to remove existing file/dir:", p);
532 std::string hashForChild(const ChildInfo& child) const
534 SGPath p(absolutePath());
535 p.append(child.name);
536 if (child.type == ChildInfo::DirectoryType) {
537 p.append(".dirindex");
539 return _repository->hashForPath(p);
542 HTTPRepoPrivate* _repository;
543 SGPath _relativePath; // in URL and file-system space
548 HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
549 _d(new HTTPRepoPrivate(this))
553 _d->rootDir = new HTTPDirectory(_d.get(), "");
554 _d->parseHashCache();
557 HTTPRepository::~HTTPRepository()
561 void HTTPRepository::setBaseUrl(const std::string &url)
566 std::string HTTPRepository::baseUrl() const
571 HTTP::Client* HTTPRepository::http() const
576 SGPath HTTPRepository::fsBase() const
581 void HTTPRepository::update()
583 if (_d->isUpdating) {
587 _d->status = REPO_NO_ERROR;
588 _d->isUpdating = true;
589 _d->failures.clear();
590 _d->updateDir(_d->rootDir, std::string(), 0);
593 bool HTTPRepository::isDoingSync() const
595 if (_d->status != REPO_NO_ERROR) {
599 return _d->isUpdating;
602 size_t HTTPRepository::bytesToDownload() const
606 HTTPRepoPrivate::RequestVector::const_iterator r;
607 for (r = _d->queuedRequests.begin(); r != _d->queuedRequests.end(); ++r) {
608 result += (*r)->contentSize();
611 for (r = _d->activeRequests.begin(); r != _d->activeRequests.end(); ++r) {
612 result += (*r)->contentSize() - (*r)->responseBytesReceived();
618 size_t HTTPRepository::bytesDownloaded() const
620 size_t result = _d->totalDownloaded;
622 HTTPRepoPrivate::RequestVector::const_iterator r;
623 for (r = _d->activeRequests.begin(); r != _d->activeRequests.end(); ++r) {
624 result += (*r)->responseBytesReceived();
630 AbstractRepository::ResultCode
631 HTTPRepository::failure() const
633 if ((_d->status == REPO_NO_ERROR) && !_d->failures.empty()) {
634 return REPO_PARTIAL_UPDATE;
640 void HTTPRepoGetRequest::cancel()
642 _directory->repository()->http->cancelRequest(this, "Reposiotry cancelled");
646 class FileGetRequest : public HTTPRepoGetRequest
649 FileGetRequest(HTTPDirectory* d, const std::string& file) :
650 HTTPRepoGetRequest(d, makeUrl(d, file)),
653 pathInRepo = _directory->absolutePath();
654 pathInRepo.append(fileName);
655 //SG_LOG(SG_TERRASYNC, SG_INFO, "will GET file " << url());
659 virtual void gotBodyData(const char* s, int n)
662 file.reset(new SGFile(pathInRepo.str()));
663 if (!file->open(SG_IO_OUT)) {
664 SG_LOG(SG_TERRASYNC, SG_WARN, "unable to create file " << pathInRepo);
665 _directory->repository()->http->cancelRequest(this, "Unable to create output file");
668 sha1_init(&hashContext);
671 sha1_write(&hashContext, s, n);
675 virtual void onDone()
678 if (responseCode() == 200) {
679 std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
680 _directory->didUpdateFile(fileName, hash, contentSize());
681 //SG_LOG(SG_TERRASYNC, SG_INFO, "got file " << fileName << " in " << _directory->absolutePath());
682 } else if (responseCode() == 404) {
683 _directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
685 _directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_HTTP);
688 _directory->repository()->finishedRequest(this);
691 virtual void onFail()
694 if (pathInRepo.exists()) {
699 _directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_SOCKET);
700 _directory->repository()->finishedRequest(this);
704 static std::string makeUrl(HTTPDirectory* d, const std::string& file)
706 return d->url() + "/" + file;
709 std::string fileName; // if empty, we're getting the directory itself
711 simgear::sha1nfo hashContext;
712 std::auto_ptr<SGFile> file;
715 class DirGetRequest : public HTTPRepoGetRequest
718 DirGetRequest(HTTPDirectory* d, const std::string& targetHash) :
719 HTTPRepoGetRequest(d, makeUrl(d)),
721 _targetHash(targetHash)
723 sha1_init(&hashContext);
724 //SG_LOG(SG_TERRASYNC, SG_INFO, "will GET dir " << url());
732 bool isRootDir() const
738 virtual void gotBodyData(const char* s, int n)
740 body += std::string(s, n);
741 sha1_write(&hashContext, s, n);
744 virtual void onDone()
746 if (responseCode() == 200) {
747 std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
748 if (!_targetHash.empty() && (hash != _targetHash)) {
749 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_CHECKSUM);
750 _directory->repository()->finishedRequest(this);
754 std::string curHash = _directory->repository()->hashForPath(path());
755 if (hash != curHash) {
756 simgear::Dir d(_directory->absolutePath());
758 if (!d.create(0700)) {
759 throw sg_io_exception("Unable to create directory", d.path());
763 // dir index data has changed, so write to disk and update
764 // the hash accordingly
765 std::ofstream of(pathInRepo().c_str(), std::ios::trunc | std::ios::out);
767 throw sg_io_exception("Failed to open directory index file for writing", pathInRepo().c_str());
770 of.write(body.data(), body.size());
772 _directory->dirIndexUpdated(hash);
774 //SG_LOG(SG_TERRASYNC, SG_INFO, "updated dir index " << _directory->absolutePath());
777 _directory->repository()->totalDownloaded += contentSize();
780 // either way we've confirmed the index is valid so update
784 _directory->updateChildrenBasedOnHash();
785 SG_LOG(SG_TERRASYNC, SG_INFO, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
786 } catch (sg_exception& ) {
787 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_IO);
789 } else if (responseCode() == 404) {
790 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
792 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_HTTP);
795 _directory->repository()->finishedRequest(this);
798 virtual void onFail()
801 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_SOCKET);
802 _directory->repository()->finishedRequest(this);
806 static std::string makeUrl(HTTPDirectory* d)
808 return d->url() + "/.dirindex";
811 SGPath pathInRepo() const
813 SGPath p(_directory->absolutePath());
814 p.append(".dirindex");
818 simgear::sha1nfo hashContext;
820 bool _isRootDir; ///< is this the repository root?
821 std::string _targetHash;
824 HTTPRepoPrivate::~HTTPRepoPrivate()
826 DirectoryVector::iterator it;
827 for (it=directories.begin(); it != directories.end(); ++it) {
831 RequestVector::iterator r;
832 for (r=activeRequests.begin(); r != activeRequests.end(); ++r) {
837 HTTP::Request_ptr HTTPRepoPrivate::updateFile(HTTPDirectory* dir, const std::string& name, size_t sz)
839 RepoRequestPtr r(new FileGetRequest(dir, name));
840 r->setContentSize(sz);
845 HTTP::Request_ptr HTTPRepoPrivate::updateDir(HTTPDirectory* dir, const std::string& hash, size_t sz)
847 RepoRequestPtr r(new DirGetRequest(dir, hash));
848 r->setContentSize(sz);
854 class HashEntryWithPath
857 HashEntryWithPath(const std::string& p) : path(p) {}
858 bool operator()(const HTTPRepoPrivate::HashCacheEntry& entry) const
859 { return entry.filePath == path; }
864 std::string HTTPRepoPrivate::hashForPath(const SGPath& p)
866 HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
867 if (it != hashes.end()) {
868 // ensure data on disk hasn't changed.
869 // we could also use the file type here if we were paranoid
870 if ((p.sizeInBytes() == it->lengthBytes) && (p.modTime() == it->modTime)) {
874 // entry in the cache, but it's stale so remove and fall through
878 std::string hash = computeHashForPath(p);
879 updatedFileContents(p, hash);
883 std::string HTTPRepoPrivate::computeHashForPath(const SGPath& p)
886 return std::string();
889 char* buf = static_cast<char*>(malloc(1024 * 1024));
892 if (!f.open(SG_IO_IN)) {
893 throw sg_io_exception("Couldn't open file for compute hash", p);
895 while ((readLen = f.read(buf, 1024 * 1024)) > 0) {
896 sha1_write(&info, buf, readLen);
901 std::string hashBytes((char*) sha1_result(&info), HASH_LENGTH);
902 return strutils::encodeHex(hashBytes);
905 void HTTPRepoPrivate::updatedFileContents(const SGPath& p, const std::string& newHash)
907 // remove the existing entry
908 HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
909 if (it != hashes.end()) {
911 hashCacheDirty = true;
914 if (newHash.empty()) {
915 return; // we're done
918 // use a cloned SGPath and reset its caching to force one stat() call
920 p2.set_cached(false);
923 HashCacheEntry entry;
924 entry.filePath = p.str();
925 entry.hashHex = newHash;
926 entry.modTime = p2.modTime();
927 entry.lengthBytes = p2.sizeInBytes();
928 hashes.push_back(entry);
930 hashCacheDirty = true;
933 void HTTPRepoPrivate::writeHashCache()
935 if (!hashCacheDirty) {
939 SGPath cachePath = basePath;
940 cachePath.append(".hashes");
942 std::ofstream stream(cachePath.c_str(),std::ios::out | std::ios::trunc);
943 HashCache::const_iterator it;
944 for (it = hashes.begin(); it != hashes.end(); ++it) {
945 stream << it->filePath << ":" << it->modTime << ":"
946 << it->lengthBytes << ":" << it->hashHex << "\n";
949 hashCacheDirty = false;
952 void HTTPRepoPrivate::parseHashCache()
955 SGPath cachePath = basePath;
956 cachePath.append(".hashes");
957 if (!cachePath.exists()) {
961 std::ifstream stream(cachePath.c_str(), std::ios::in);
965 while (!stream.eof()) {
966 stream.getline(buf, 2048);
968 char* nameData = ::strtok_r(buf, ":", &lastToken);
969 char* timeData = ::strtok_r(NULL, ":", &lastToken);
970 char* sizeData = ::strtok_r(NULL, ":", &lastToken);
971 char* hashData = ::strtok_r(NULL, ":", &lastToken);
972 if (!nameData || !timeData || !sizeData || !hashData) {
976 HashCacheEntry entry;
977 entry.filePath = nameData;
978 entry.hashHex = hashData;
979 entry.modTime = strtol(timeData, NULL, 10);
980 entry.lengthBytes = strtol(sizeData, NULL, 10);
981 hashes.push_back(entry);
985 class DirectoryWithPath
988 DirectoryWithPath(const std::string& p) : path(p) {}
989 bool operator()(const HTTPDirectory* entry) const
990 { return entry->relativePath().str() == path; }
995 HTTPDirectory* HTTPRepoPrivate::getOrCreateDirectory(const std::string& path)
997 DirectoryWithPath p(path);
998 DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p);
999 if (it != directories.end()) {
1003 HTTPDirectory* d = new HTTPDirectory(this, path);
1004 directories.push_back(d);
1008 bool HTTPRepoPrivate::deleteDirectory(const std::string& path)
1010 DirectoryWithPath p(path);
1011 DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p);
1012 if (it != directories.end()) {
1013 HTTPDirectory* d = *it;
1014 directories.erase(it);
1015 Dir dir(d->absolutePath());
1016 bool result = dir.remove(true);
1019 // update the hash cache too
1020 updatedFileContents(path, std::string());
1028 void HTTPRepoPrivate::makeRequest(RepoRequestPtr req)
1030 if (activeRequests.size() > 4) {
1031 queuedRequests.push_back(req);
1033 activeRequests.push_back(req);
1034 http->makeRequest(req);
1038 void HTTPRepoPrivate::finishedRequest(const RepoRequestPtr& req)
1040 RequestVector::iterator it = std::find(activeRequests.begin(), activeRequests.end(), req);
1041 if (it == activeRequests.end()) {
1042 throw sg_exception("lost request somehow", req->url());
1044 activeRequests.erase(it);
1046 if (!queuedRequests.empty()) {
1047 RepoRequestPtr rr = queuedRequests.front();
1048 queuedRequests.erase(queuedRequests.begin());
1049 activeRequests.push_back(rr);
1050 http->makeRequest(rr);
1055 if (activeRequests.empty() && queuedRequests.empty()) {
1060 void HTTPRepoPrivate::failedToGetRootIndex(AbstractRepository::ResultCode st)
1062 SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl);
1066 void HTTPRepoPrivate::failedToUpdateChild(const SGPath& relativePath,
1067 AbstractRepository::ResultCode fileStatus)
1070 f.path = relativePath;
1071 f.error = fileStatus;
1072 failures.push_back(f);
1074 SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " code:" << fileStatus);
1079 } // of namespace simgear