diff --git a/source/dub/dub.d b/source/dub/dub.d index 8bcc557..d47632f 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -668,6 +668,8 @@ foreach(ps; m_packageSuppliers){ try { pinfo = ps.fetchPackageRecipe(packageId, dep, (options & FetchOptions.usePrerelease) != 0); + if (pinfo.type == Json.Type.null_) + continue; supplier = ps; break; } catch(Exception e) { @@ -948,8 +950,15 @@ */ auto searchPackages(string query) { - return m_packageSuppliers.map!(ps => tuple(ps.description, ps.searchPackages(query))).array - .filter!(t => t[1].length); + Tuple!(string, PackageSupplier.SearchResult[])[] results; + foreach (ps; this.m_packageSuppliers) { + try + results ~= tuple(ps.description, ps.searchPackages(query)); + catch (Exception e) { + logWarn("Searching %s for '%s' failed: %s", ps.description, query, e.msg); + } + } + return results.filter!(tup => tup[1].length); } /** Returns a list of all available versions (including branches) for a @@ -1419,6 +1428,8 @@ if (rootpack == name) { try { auto desc = ps.fetchPackageRecipe(name, dep, prerelease); + if (desc.type == Json.Type.null_) + continue; auto ret = new Package(desc); m_remotePackages[key] = ret; return ret; diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index 799755b..e1e2175 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -16,7 +16,7 @@ // todo: cleanup imports. import core.thread; -import std.algorithm : startsWith; +import std.algorithm : canFind, startsWith; import std.array; import std.conv; import std.exception; @@ -199,6 +199,38 @@ } } +version(DubUseCurl) { + /++ + Exception thrown on HTTP request failures, e.g. 404 Not Found. + +/ + static if (__VERSION__ <= 2075) class HTTPStatusException : CurlException + { + /++ + Params: + status = The HTTP status code. + msg = The message for the exception. + file = The file where the exception occurred. + line = The line number where the exception occurred. + next = The previous exception in the chain of exceptions, if any. + +/ + @safe pure nothrow + this( + int status, + string msg, + string file = __FILE__, + size_t line = __LINE__, + Throwable next = null) + { + this.status = status; + super(msg, file, line, next); + } + + int status; /// The HTTP status code + } +} else version (Have_vibe_d_http) { + public import vibe.http.common : HTTPStatusException; +} + /** Downloads a file from the specified URL. @@ -211,10 +243,19 @@ auto conn = HTTP(); setupHTTPClient(conn); logDebug("Storing %s...", url); - std.net.curl.download(url, filename, conn); - enforce(conn.statusLine.code < 400, - format("Failed to download %s: %s %s", - url, conn.statusLine.code, conn.statusLine.reason)); + static if (__VERSION__ <= 2075) + { + try + std.net.curl.download(url, filename, conn); + catch (CurlException e) + { + if (e.msg.canFind("404")) + throw new HTTPStatusException(404, e.msg); + throw e; + } + } + else + std.net.curl.download(url, filename, conn); } else version (Have_vibe_d) { import vibe.inet.urltransfer; vibe.inet.urltransfer.download(url, filename); @@ -232,11 +273,19 @@ auto conn = HTTP(); setupHTTPClient(conn); logDebug("Getting %s...", url); - auto ret = cast(ubyte[])get(url, conn); - enforce(conn.statusLine.code < 400, - format("Failed to GET %s: %s %s", - url, conn.statusLine.code, conn.statusLine.reason)); - return ret; + static if (__VERSION__ <= 2075) + { + try + return cast(ubyte[])get(url, conn); + catch (CurlException e) + { + if (e.msg.canFind("404")) + throw new HTTPStatusException(404, e.msg); + throw e; + } + } + else + return cast(ubyte[])get(url, conn); } else version (Have_vibe_d) { import vibe.inet.urltransfer; import vibe.stream.operations; diff --git a/source/dub/packagesupplier.d b/source/dub/packagesupplier.d index 0e2da45..cf12f3c 100644 --- a/source/dub/packagesupplier.d +++ b/source/dub/packagesupplier.d @@ -170,8 +170,10 @@ Version[] getVersions(string package_id) { + auto md = getMetadata(package_id); + if (md.type == Json.Type.null_) + return null; Version[] ret; - Json md = getMetadata(package_id); foreach (json; md["versions"]) { auto cur = Version(cast(string)json["version"]); ret ~= cur; @@ -184,6 +186,8 @@ { import std.array : replace; Json best = getBestPackage(packageId, dep, pre_release); + if (best.type == Json.Type.null_) + return; auto vers = best["version"].get!string; auto url = m_registryUrl ~ Path(PackagesPath~"/"~packageId~"/"~vers~".zip"); logDiagnostic("Downloading from '%s'", url); @@ -209,7 +213,16 @@ logDebug("Downloading metadata for %s", packageId); logDebug("Getting from %s", url); - auto jsonData = cast(string)download(url); + string jsonData; + try + jsonData = cast(string)download(url); + catch (HTTPStatusException e) + { + if (e.status != 404) + throw e; + logDebug("Package %s not found in %s: %s", packageId, description, e.msg); + return Json(null); + } Json json = parseJsonString(jsonData, url.toString()); // strip readme data (to save size and time) foreach (ref v; json["versions"]) @@ -223,12 +236,7 @@ auto url = m_registryUrl; url.localURI = "/api/packages/search?q="~encodeComponent(query); string data; - try - data = cast(string)download(url); - catch (Exception e) { - logWarn("Searching %s for '%s' failed: %s", m_registryUrl, query, e.msg); - return null; - } + data = cast(string)download(url); import std.algorithm : map; return data.parseJson.opt!(Json[]) .map!(j => SearchResult(j["name"].opt!string, j["description"].opt!string, j["version"].opt!string)) @@ -238,6 +246,8 @@ private Json getBestPackage(string packageId, Dependency dep, bool pre_release) { Json md = getMetadata(packageId); + if (md.type == Json.Type.null_) + return md; Json best = null; Version bestver; foreach (json; md["versions"]) {