Index: utils/pub/hosted_source.dart |
diff --git a/utils/pub/hosted_source.dart b/utils/pub/hosted_source.dart |
index 6fdc8e91328b745441897ab13fdceb0a8a724f4b..44f7195ea5ffe8cbd3bf098a2de3c09112fbf945 100644 |
--- a/utils/pub/hosted_source.dart |
+++ b/utils/pub/hosted_source.dart |
@@ -29,21 +29,20 @@ class HostedSource extends Source { |
final name = "hosted"; |
final shouldCache = true; |
- /// The URL of the default package repository. |
- static final defaultUrl = "https://pub.dartlang.org"; |
- |
/// Downloads a list of all versions of a package that are available from the |
/// site. |
Future<List<Version>> getVersions(String name, description) { |
- var parsed = _parseDescription(description); |
- var fullUrl = "${parsed.last}/packages/${parsed.first}.json"; |
+ var url = _makeUrl(description, |
+ (server, package) => "$server/packages/$package.json"); |
- return httpClient.read(fullUrl).then((body) { |
+ log.io("Get versions from $url."); |
+ return httpClient.read(url).then((body) { |
var doc = json.parse(body); |
return doc['versions'] |
.map((version) => new Version.parse(version)) |
.toList(); |
}).catchError((ex) { |
+ var parsed = _parseDescription(description); |
_throwFriendlyError(ex, parsed.first, parsed.last); |
}); |
} |
@@ -51,13 +50,14 @@ class HostedSource extends Source { |
/// Downloads and parses the pubspec for a specific version of a package that |
/// is available from the site. |
Future<Pubspec> describe(PackageId id) { |
- var parsed = _parseDescription(id.description); |
- var fullUrl = "${parsed.last}/packages/${parsed.first}/versions/" |
- "${id.version}.yaml"; |
+ var url = _makeVersionUrl(id, (server, package, version) => |
+ "$server/packages/$package/versions/$version.yaml"); |
- return httpClient.read(fullUrl).then((yaml) { |
+ log.io("Describe package at $url."); |
+ return httpClient.read(url).then((yaml) { |
return new Pubspec.parse(null, yaml, systemCache.sources); |
}).catchError((ex) { |
+ var parsed = _parseDescription(id.description); |
_throwFriendlyError(ex, id, parsed.last); |
}); |
} |
@@ -65,21 +65,19 @@ class HostedSource extends Source { |
/// Downloads a package from the site and unpacks it. |
Future<bool> install(PackageId id, String destPath) { |
return defer(() { |
- var parsedDescription = _parseDescription(id.description); |
- var name = parsedDescription.first; |
- var url = parsedDescription.last; |
- |
- var fullUrl = "$url/packages/$name/versions/${id.version}.tar.gz"; |
+ var url = _makeVersionUrl(id, (server, package, version) => |
+ "$server/packages/$package/versions/$version.tar.gz"); |
+ log.io("Install package from $url."); |
log.message('Downloading $id...'); |
// Download and extract the archive to a temp directory. |
var tempDir = systemCache.createTempDir(); |
- return httpClient.send(new http.Request("GET", Uri.parse(fullUrl))) |
+ return httpClient.send(new http.Request("GET", url)) |
.then((response) => response.stream) |
.then((stream) { |
return timeout(extractTarGz(stream, tempDir), HTTP_TIMEOUT, |
- 'fetching URL "$fullUrl"'); |
+ 'fetching URL "$url"'); |
}).then((_) { |
// Now that the install has succeeded, move it to the real location in |
// the cache. This ensures that we don't leave half-busted ghost |
@@ -142,31 +140,57 @@ class HostedSource extends Source { |
throw asyncError; |
} |
- /// Parses the description for a package. |
- /// |
- /// If the package parses correctly, this returns a (name, url) pair. If not, |
- /// this throws a descriptive FormatException. |
- Pair<String, String> _parseDescription(description) { |
- if (description is String) { |
- return new Pair<String, String>(description, defaultUrl); |
- } |
+} |
- if (description is! Map) { |
- throw new FormatException( |
- "The description must be a package name or map."); |
- } |
+/// The URL of the default package repository. |
+final _defaultUrl = "https://pub.dartlang.org"; |
+ |
+/// Parses [description] into its server and package name components, then |
+/// converts that to a Uri given [pattern]. Ensures the package name is |
+/// properly URL encoded. |
+Uri _makeUrl(description, String pattern(String server, String package)) { |
+ var parsed = _parseDescription(description); |
+ var server = parsed.last; |
+ var package = encodeUriComponent(parsed.first); |
+ return new Uri(pattern(server, package)); |
+} |
- if (!description.containsKey("name")) { |
- throw new FormatException( |
- "The description map must contain a 'name' key."); |
- } |
+/// Parses [id] into its server, package name, and version components, then |
+/// converts that to a Uri given [pattern]. Ensures the package name is |
+/// properly URL encoded. |
+Uri _makeVersionUrl(PackageId id, |
+ String pattern(String server, String package, String version)) { |
+ var parsed = _parseDescription(id.description); |
+ var server = parsed.last; |
+ var package = encodeUriComponent(parsed.first); |
+ var version = encodeUriComponent(id.version.toString()); |
+ return new Uri(pattern(server, package, version)); |
+} |
- var name = description["name"]; |
- if (name is! String) { |
- throw new FormatException("The 'name' key must have a string value."); |
- } |
+/// Parses the description for a package. |
+/// |
+/// If the package parses correctly, this returns a (name, url) pair. If not, |
+/// this throws a descriptive FormatException. |
+Pair<String, String> _parseDescription(description) { |
+ if (description is String) { |
+ return new Pair<String, String>(description, _defaultUrl); |
+ } |
+ |
+ if (description is! Map) { |
+ throw new FormatException( |
+ "The description must be a package name or map."); |
+ } |
+ |
+ if (!description.containsKey("name")) { |
+ throw new FormatException( |
+ "The description map must contain a 'name' key."); |
+ } |
- var url = description.containsKey("url") ? description["url"] : defaultUrl; |
- return new Pair<String, String>(name, url); |
+ var name = description["name"]; |
+ if (name is! String) { |
+ throw new FormatException("The 'name' key must have a string value."); |
} |
+ |
+ var url = description.containsKey("url") ? description["url"] : _defaultUrl; |
+ return new Pair<String, String>(name, url); |
} |