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>
35 #include "simgear/debug/logstream.hxx"
36 #include "simgear/misc/strutils.hxx"
37 #include <simgear/misc/sg_dir.hxx>
38 #include <simgear/io/HTTPClient.hxx>
39 #include <simgear/io/sg_file.hxx>
40 #include <simgear/misc/sgstream.hxx>
41 #include <simgear/structure/exception.hxx>
42 #include <simgear/timing/timestamp.hxx>
44 #include <simgear/misc/sg_hash.hxx>
51 class HTTPRepoGetRequest : public HTTP::Request
54 HTTPRepoGetRequest(HTTPDirectory* d, const std::string& u) :
60 virtual void cancel();
62 size_t contentSize() const
67 void setContentSize(size_t sz)
72 HTTPDirectory* _directory;
76 typedef SGSharedPtr<HTTPRepoGetRequest> RepoRequestPtr;
90 typedef std::vector<HashCacheEntry> HashCache;
97 AbstractRepository::ResultCode error;
100 typedef std::vector<Failure> FailureList;
101 FailureList failures;
103 HTTPRepoPrivate(HTTPRepository* parent) :
104 hashCacheDirty(false),
107 status(AbstractRepository::REPO_NO_ERROR),
113 HTTPRepository* p; // link back to outer
118 AbstractRepository::ResultCode status;
119 HTTPDirectory* rootDir;
120 size_t totalDownloaded;
122 HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name,
124 HTTP::Request_ptr updateDir(HTTPDirectory* dir, const std::string& hash,
127 std::string hashForPath(const SGPath& p);
128 void updatedFileContents(const SGPath& p, const std::string& newHash);
129 void parseHashCache();
130 std::string computeHashForPath(const SGPath& p);
131 void writeHashCache();
133 void failedToGetRootIndex(AbstractRepository::ResultCode st);
134 void failedToUpdateChild(const SGPath& relativePath,
135 AbstractRepository::ResultCode fileStatus);
137 typedef std::vector<RepoRequestPtr> RequestVector;
138 RequestVector queuedRequests,
141 void makeRequest(RepoRequestPtr req);
142 void finishedRequest(const RepoRequestPtr& req);
144 HTTPDirectory* getOrCreateDirectory(const std::string& path);
145 bool deleteDirectory(const std::string& path);
147 typedef std::vector<HTTPDirectory*> DirectoryVector;
148 DirectoryVector directories;
162 ChildInfo(Type ty, const std::string & nameData, const std::string & hashData) :
170 ChildInfo(const ChildInfo& other) :
174 sizeInBytes(other.sizeInBytes)
177 void setSize(const std::string & sizeData)
179 sizeInBytes = ::strtol(sizeData.c_str(), NULL, 10);
182 bool operator<(const ChildInfo& other) const
184 return name < other.name;
188 std::string name, hash;
192 typedef std::vector<ChildInfo> ChildInfoList;
193 ChildInfoList children;
196 HTTPDirectory(HTTPRepoPrivate* repo, const std::string& path) :
202 SGPath p(absolutePath());
205 // already exists on disk
206 parseDirIndex(children);
207 std::sort(children.begin(), children.end());
208 } catch (sg_exception& ) {
209 // parsing cache failed
215 HTTPRepoPrivate* repository() const
220 std::string url() const
222 if (_relativePath.str().empty()) {
223 return _repository->baseUrl;
226 return _repository->baseUrl + "/" + _relativePath.str();
229 void dirIndexUpdated(const std::string& hash)
231 SGPath fpath(_relativePath);
232 fpath.append(".dirindex");
233 _repository->updatedFileContents(fpath, hash);
236 parseDirIndex(children);
237 std::sort(children.begin(), children.end());
240 void failedToUpdate(AbstractRepository::ResultCode status)
242 if (_relativePath.isNull()) {
244 _repository->failedToGetRootIndex(status);
246 _repository->failedToUpdateChild(_relativePath, status);
250 void updateChildrenBasedOnHash()
252 //SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
254 string_list indexNames = indexChildren(),
255 toBeUpdated, orphans;
256 simgear::Dir d(absolutePath());
257 SG_LOG(SG_TERRASYNC, SG_DEBUG, "Dir created for: '" << absolutePath() );
258 PathList fsChildren = d.children(0);
259 SG_LOG(SG_TERRASYNC, SG_DEBUG, "Dir has children: '" << fsChildren.size() );
260 PathList::const_iterator it = fsChildren.begin();
263 for (; it != fsChildren.end(); ++it) {
264 SG_LOG(SG_TERRASYNC, SG_DEBUG, "processing child: '" << it->str() << "', file=" << it->file() << ",isDir=" << it->isDir() );
265 ChildInfo info(it->isDir() ? ChildInfo::DirectoryType : ChildInfo::FileType,
267 std::string hash = hashForChild(info);
268 SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash is: '" << hash << "'" );
270 ChildInfoList::iterator c = findIndexChild(it->file());
271 if (c == children.end()) {
272 SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" );
273 orphans.push_back(it->file());
274 } else if (c->hash != hash) {
275 SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() << "', c->name=" << c->name );
276 // file exists, but hash mismatch, schedule update
278 SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists but hash is wrong for:" << c->name);
279 SG_LOG(SG_TERRASYNC, SG_DEBUG, "on disk:" << hash << " vs in info:" << c->hash);
282 toBeUpdated.push_back(c->name);
284 // file exists and hash is valid. If it's a directory,
285 // perform a recursive check.
286 SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists hash is good:" << c->name);
287 if (c->type == ChildInfo::DirectoryType) {
288 SG_LOG(SG_TERRASYNC, SG_DEBUG, "going recursive for:" << c->name);
289 SGPath p(relativePath());
291 HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
292 childDir->updateChildrenBasedOnHash();
296 // remove existing file system children from the index list,
297 // so we can detect new children
298 SG_LOG(SG_TERRASYNC, SG_DEBUG, "looking for name in indexNames:" << c->name);
299 string_list::iterator it = std::find(indexNames.begin(), indexNames.end(), c->name);
300 if (it != indexNames.end()) {
301 SG_LOG(SG_TERRASYNC, SG_DEBUG, "found name in indexNames, erasing:" << c->name);
302 indexNames.erase(it);
304 } // of real children iteration
306 // all remaining names in indexChilden are new children
307 toBeUpdated.insert(toBeUpdated.end(), indexNames.begin(), indexNames.end());
309 removeOrphans(orphans);
310 scheduleUpdates(toBeUpdated);
313 void removeOrphans(const string_list& orphans)
315 string_list::const_iterator it;
316 for (it = orphans.begin(); it != orphans.end(); ++it) {
321 string_list indexChildren() const
324 r.reserve(children.size());
325 ChildInfoList::const_iterator it;
326 for (it=children.begin(); it != children.end(); ++it) {
327 r.push_back(it->name);
332 void scheduleUpdates(const string_list& names)
334 string_list::const_iterator it;
335 for (it = names.begin(); it != names.end(); ++it) {
336 ChildInfoList::iterator cit = findIndexChild(*it);
337 if (cit == children.end()) {
338 SG_LOG(SG_TERRASYNC, SG_WARN, "scheduleUpdate, unknown child:" << *it);
342 if (cit->type == ChildInfo::FileType) {
343 _repository->updateFile(this, *it, cit->sizeInBytes);
345 SGPath p(relativePath());
347 HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
348 _repository->updateDir(childDir, cit->hash, cit->sizeInBytes);
353 SGPath absolutePath() const
355 SGPath r(_repository->basePath);
356 r.append(_relativePath.str());
360 SGPath relativePath() const
362 return _relativePath;
365 void didUpdateFile(const std::string& file, const std::string& hash, size_t sz)
367 // check hash matches what we expected
368 ChildInfoList::iterator it = findIndexChild(file);
369 if (it == children.end()) {
370 SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
372 SGPath fpath(_relativePath);
375 if (it->hash != hash) {
376 _repository->failedToUpdateChild(_relativePath, AbstractRepository::REPO_ERROR_CHECKSUM);
378 _repository->updatedFileContents(fpath, hash);
379 _repository->totalDownloaded += sz;
380 //SG_LOG(SG_TERRASYNC, SG_INFO, "did update:" << fpath);
382 } // of found in child list
385 void didFailToUpdateFile(const std::string& file,
386 AbstractRepository::ResultCode status)
388 SGPath fpath(_relativePath);
390 _repository->failedToUpdateChild(fpath, status);
396 ChildWithName(const std::string& n) : name(n) {}
399 bool operator()(const ChildInfo& info) const
400 { return info.name == name; }
403 ChildInfoList::iterator findIndexChild(const std::string& name)
405 return std::find_if(children.begin(), children.end(), ChildWithName(name));
408 bool parseDirIndex(ChildInfoList& children)
410 SGPath p(absolutePath());
411 p.append(".dirindex");
416 std::ifstream indexStream( p.c_str(), std::ios::in );
418 if ( !indexStream.is_open() ) {
419 throw sg_io_exception("cannot open dirIndex file", p);
422 while (!indexStream.eof() ) {
424 std::getline( indexStream, line );
425 line = simgear::strutils::strip(line);
427 // skip blank line or comment beginning with '#'
428 if( line.empty() || line[0] == '#' )
431 string_list tokens = simgear::strutils::split( line, ":" );
433 std::string typeData = tokens[0];
435 if( typeData == "version" ) {
436 if( tokens.size() < 2 ) {
437 SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: missing version number in line '" << line << "'" );
440 if( tokens[1] != "1" ) {
441 SG_LOG(SG_TERRASYNC, SG_WARN, "invalid .dirindex file: wrong version number '" << tokens[1] << "' (expected 1)" );
446 if( typeData == "path" ) {
447 continue; // ignore path, next line
450 if( tokens.size() < 3 ) {
451 SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: not enough tokens in line '" << line << "' (ignoring line)" );
455 if (typeData != "f" && typeData != "d" ) {
456 SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid type in line '" << line << "', expected 'd' or 'f', (ignoring line)" );
459 children.push_back(ChildInfo(typeData == "f" ? ChildInfo::FileType : ChildInfo::DirectoryType, tokens[1], tokens[2]));
461 if (tokens.size() > 3) {
462 children.back().setSize(tokens[3]);
469 void removeChild(const std::string& name)
471 SGPath p(absolutePath());
475 SGPath fpath(_relativePath);
479 ok = _repository->deleteDirectory(fpath.str());
481 // remove the hash cache entry
482 _repository->updatedFileContents(fpath, std::string());
487 SG_LOG(SG_TERRASYNC, SG_WARN, "removal failed for:" << p);
488 throw sg_io_exception("Failed to remove existing file/dir:", p);
492 std::string hashForChild(const ChildInfo& child) const
494 SGPath p(absolutePath());
495 p.append(child.name);
496 if (child.type == ChildInfo::DirectoryType) {
497 p.append(".dirindex");
499 return _repository->hashForPath(p);
502 HTTPRepoPrivate* _repository;
503 SGPath _relativePath; // in URL and file-system space
508 HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
509 _d(new HTTPRepoPrivate(this))
513 _d->rootDir = new HTTPDirectory(_d.get(), "");
514 _d->parseHashCache();
517 HTTPRepository::~HTTPRepository()
521 void HTTPRepository::setBaseUrl(const std::string &url)
526 std::string HTTPRepository::baseUrl() const
531 HTTP::Client* HTTPRepository::http() const
536 SGPath HTTPRepository::fsBase() const
541 void HTTPRepository::update()
543 if (_d->isUpdating) {
547 _d->status = REPO_NO_ERROR;
548 _d->isUpdating = true;
549 _d->failures.clear();
550 _d->updateDir(_d->rootDir, std::string(), 0);
553 bool HTTPRepository::isDoingSync() const
555 if (_d->status != REPO_NO_ERROR) {
559 return _d->isUpdating;
562 size_t HTTPRepository::bytesToDownload() const
566 HTTPRepoPrivate::RequestVector::const_iterator r;
567 for (r = _d->queuedRequests.begin(); r != _d->queuedRequests.end(); ++r) {
568 result += (*r)->contentSize();
571 for (r = _d->activeRequests.begin(); r != _d->activeRequests.end(); ++r) {
572 result += (*r)->contentSize() - (*r)->responseBytesReceived();
578 size_t HTTPRepository::bytesDownloaded() const
580 size_t result = _d->totalDownloaded;
582 HTTPRepoPrivate::RequestVector::const_iterator r;
583 for (r = _d->activeRequests.begin(); r != _d->activeRequests.end(); ++r) {
584 result += (*r)->responseBytesReceived();
590 AbstractRepository::ResultCode
591 HTTPRepository::failure() const
593 if ((_d->status == REPO_NO_ERROR) && !_d->failures.empty()) {
594 return REPO_PARTIAL_UPDATE;
600 void HTTPRepoGetRequest::cancel()
602 _directory->repository()->http->cancelRequest(this, "Reposiotry cancelled");
606 class FileGetRequest : public HTTPRepoGetRequest
609 FileGetRequest(HTTPDirectory* d, const std::string& file) :
610 HTTPRepoGetRequest(d, makeUrl(d, file)),
613 pathInRepo = _directory->absolutePath();
614 pathInRepo.append(fileName);
615 //SG_LOG(SG_TERRASYNC, SG_INFO, "will GET file " << url());
619 virtual void gotBodyData(const char* s, int n)
622 file.reset(new SGFile(pathInRepo.str()));
623 if (!file->open(SG_IO_OUT)) {
624 SG_LOG(SG_TERRASYNC, SG_WARN, "unable to create file " << pathInRepo);
625 _directory->repository()->http->cancelRequest(this, "Unable to create output file");
628 sha1_init(&hashContext);
631 sha1_write(&hashContext, s, n);
635 virtual void onDone()
638 if (responseCode() == 200) {
639 std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
640 _directory->didUpdateFile(fileName, hash, contentSize());
641 //SG_LOG(SG_TERRASYNC, SG_INFO, "got file " << fileName << " in " << _directory->absolutePath());
642 } else if (responseCode() == 404) {
643 _directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
645 _directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_HTTP);
648 _directory->repository()->finishedRequest(this);
651 virtual void onFail()
654 if (pathInRepo.exists()) {
659 _directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_SOCKET);
660 _directory->repository()->finishedRequest(this);
664 static std::string makeUrl(HTTPDirectory* d, const std::string& file)
666 return d->url() + "/" + file;
669 std::string fileName; // if empty, we're getting the directory itself
671 simgear::sha1nfo hashContext;
672 std::auto_ptr<SGFile> file;
675 class DirGetRequest : public HTTPRepoGetRequest
678 DirGetRequest(HTTPDirectory* d, const std::string& targetHash) :
679 HTTPRepoGetRequest(d, makeUrl(d)),
681 _targetHash(targetHash)
683 sha1_init(&hashContext);
684 //SG_LOG(SG_TERRASYNC, SG_INFO, "will GET dir " << url());
692 bool isRootDir() const
698 virtual void gotBodyData(const char* s, int n)
700 body += std::string(s, n);
701 sha1_write(&hashContext, s, n);
704 virtual void onDone()
706 if (responseCode() == 200) {
707 std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
708 if (!_targetHash.empty() && (hash != _targetHash)) {
709 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_CHECKSUM);
710 _directory->repository()->finishedRequest(this);
714 std::string curHash = _directory->repository()->hashForPath(path());
715 if (hash != curHash) {
716 simgear::Dir d(_directory->absolutePath());
718 if (!d.create(0700)) {
719 throw sg_io_exception("Unable to create directory", d.path());
723 // dir index data has changed, so write to disk and update
724 // the hash accordingly
725 std::ofstream of(pathInRepo().c_str(), std::ios::trunc | std::ios::out);
727 throw sg_io_exception("Failed to open directory index file for writing", pathInRepo().c_str());
730 of.write(body.data(), body.size());
732 _directory->dirIndexUpdated(hash);
734 //SG_LOG(SG_TERRASYNC, SG_INFO, "updated dir index " << _directory->absolutePath());
737 _directory->repository()->totalDownloaded += contentSize();
740 // either way we've confirmed the index is valid so update
744 _directory->updateChildrenBasedOnHash();
745 SG_LOG(SG_TERRASYNC, SG_INFO, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
746 } catch (sg_exception& ) {
747 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_IO);
749 } else if (responseCode() == 404) {
750 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
752 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_HTTP);
755 _directory->repository()->finishedRequest(this);
758 virtual void onFail()
761 _directory->failedToUpdate(AbstractRepository::REPO_ERROR_SOCKET);
762 _directory->repository()->finishedRequest(this);
766 static std::string makeUrl(HTTPDirectory* d)
768 return d->url() + "/.dirindex";
771 SGPath pathInRepo() const
773 SGPath p(_directory->absolutePath());
774 p.append(".dirindex");
778 simgear::sha1nfo hashContext;
780 bool _isRootDir; ///< is this the repository root?
781 std::string _targetHash;
784 HTTPRepoPrivate::~HTTPRepoPrivate()
786 DirectoryVector::iterator it;
787 for (it=directories.begin(); it != directories.end(); ++it) {
791 RequestVector::iterator r;
792 for (r=activeRequests.begin(); r != activeRequests.end(); ++r) {
797 HTTP::Request_ptr HTTPRepoPrivate::updateFile(HTTPDirectory* dir, const std::string& name, size_t sz)
799 RepoRequestPtr r(new FileGetRequest(dir, name));
800 r->setContentSize(sz);
805 HTTP::Request_ptr HTTPRepoPrivate::updateDir(HTTPDirectory* dir, const std::string& hash, size_t sz)
807 RepoRequestPtr r(new DirGetRequest(dir, hash));
808 r->setContentSize(sz);
814 class HashEntryWithPath
817 HashEntryWithPath(const std::string& p) : path(p) {}
818 bool operator()(const HTTPRepoPrivate::HashCacheEntry& entry) const
819 { return entry.filePath == path; }
824 std::string HTTPRepoPrivate::hashForPath(const SGPath& p)
826 HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
827 if (it != hashes.end()) {
828 // ensure data on disk hasn't changed.
829 // we could also use the file type here if we were paranoid
830 if ((p.sizeInBytes() == it->lengthBytes) && (p.modTime() == it->modTime)) {
834 // entry in the cache, but it's stale so remove and fall through
838 std::string hash = computeHashForPath(p);
839 updatedFileContents(p, hash);
843 std::string HTTPRepoPrivate::computeHashForPath(const SGPath& p)
846 return std::string();
849 char* buf = static_cast<char*>(malloc(1024 * 1024));
852 if (!f.open(SG_IO_IN)) {
853 throw sg_io_exception("Couldn't open file for compute hash", p);
855 while ((readLen = f.read(buf, 1024 * 1024)) > 0) {
856 sha1_write(&info, buf, readLen);
861 std::string hashBytes((char*) sha1_result(&info), HASH_LENGTH);
862 return strutils::encodeHex(hashBytes);
865 void HTTPRepoPrivate::updatedFileContents(const SGPath& p, const std::string& newHash)
867 // remove the existing entry
868 HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
869 if (it != hashes.end()) {
871 hashCacheDirty = true;
874 if (newHash.empty()) {
875 return; // we're done
878 // use a cloned SGPath and reset its caching to force one stat() call
880 p2.set_cached(false);
883 HashCacheEntry entry;
884 entry.filePath = p.str();
885 entry.hashHex = newHash;
886 entry.modTime = p2.modTime();
887 entry.lengthBytes = p2.sizeInBytes();
888 hashes.push_back(entry);
890 hashCacheDirty = true;
893 void HTTPRepoPrivate::writeHashCache()
895 if (!hashCacheDirty) {
899 SGPath cachePath = basePath;
900 cachePath.append(".hashes");
902 std::ofstream stream(cachePath.c_str(),std::ios::out | std::ios::trunc);
903 HashCache::const_iterator it;
904 for (it = hashes.begin(); it != hashes.end(); ++it) {
905 stream << it->filePath << ":" << it->modTime << ":"
906 << it->lengthBytes << ":" << it->hashHex << "\n";
909 hashCacheDirty = false;
912 void HTTPRepoPrivate::parseHashCache()
915 SGPath cachePath = basePath;
916 cachePath.append(".hashes");
917 if (!cachePath.exists()) {
921 std::ifstream stream(cachePath.c_str(), std::ios::in);
923 while (!stream.eof()) {
925 std::getline(stream,line);
926 line = simgear::strutils::strip(line);
927 if( line.empty() || line[0] == '#' )
930 string_list tokens = simgear::strutils::split( line, ":" );
931 if( tokens.size() < 4 ) {
932 SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath.str() << "': '" << line << "' (ignoring line)");
935 const std::string nameData = simgear::strutils::strip(tokens[0]);
936 const std::string timeData = simgear::strutils::strip(tokens[1]);
937 const std::string sizeData = simgear::strutils::strip(tokens[2]);
938 const std::string hashData = simgear::strutils::strip(tokens[3]);
940 if (nameData.empty() || timeData.empty() || sizeData.empty() || hashData.empty() ) {
941 SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath.str() << "': '" << line << "' (ignoring line)");
945 HashCacheEntry entry;
946 entry.filePath = nameData;
947 entry.hashHex = hashData;
948 entry.modTime = strtol(timeData.c_str(), NULL, 10);
949 entry.lengthBytes = strtol(sizeData.c_str(), NULL, 10);
950 hashes.push_back(entry);
954 class DirectoryWithPath
957 DirectoryWithPath(const std::string& p) : path(p) {}
958 bool operator()(const HTTPDirectory* entry) const
959 { return entry->relativePath().str() == path; }
964 HTTPDirectory* HTTPRepoPrivate::getOrCreateDirectory(const std::string& path)
966 DirectoryWithPath p(path);
967 DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p);
968 if (it != directories.end()) {
972 HTTPDirectory* d = new HTTPDirectory(this, path);
973 directories.push_back(d);
977 bool HTTPRepoPrivate::deleteDirectory(const std::string& path)
979 DirectoryWithPath p(path);
980 DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p);
981 if (it != directories.end()) {
982 HTTPDirectory* d = *it;
983 directories.erase(it);
984 Dir dir(d->absolutePath());
985 bool result = dir.remove(true);
988 // update the hash cache too
989 updatedFileContents(path, std::string());
997 void HTTPRepoPrivate::makeRequest(RepoRequestPtr req)
999 if (activeRequests.size() > 4) {
1000 queuedRequests.push_back(req);
1002 activeRequests.push_back(req);
1003 http->makeRequest(req);
1007 void HTTPRepoPrivate::finishedRequest(const RepoRequestPtr& req)
1009 RequestVector::iterator it = std::find(activeRequests.begin(), activeRequests.end(), req);
1010 if (it == activeRequests.end()) {
1011 throw sg_exception("lost request somehow", req->url());
1013 activeRequests.erase(it);
1015 if (!queuedRequests.empty()) {
1016 RepoRequestPtr rr = queuedRequests.front();
1017 queuedRequests.erase(queuedRequests.begin());
1018 activeRequests.push_back(rr);
1019 http->makeRequest(rr);
1024 if (activeRequests.empty() && queuedRequests.empty()) {
1029 void HTTPRepoPrivate::failedToGetRootIndex(AbstractRepository::ResultCode st)
1031 SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl);
1035 void HTTPRepoPrivate::failedToUpdateChild(const SGPath& relativePath,
1036 AbstractRepository::ResultCode fileStatus)
1039 f.path = relativePath;
1040 f.error = fileStatus;
1041 failures.push_back(f);
1043 SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " code:" << fileStatus);
1048 } // of namespace simgear