Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: lib/src/source/hosted.dart

Issue 2028853002: Properly handle HTTP hosted URLs. (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:io' as io; 6 import 'dart:io' as io;
7 import "dart:convert"; 7 import "dart:convert";
8 8
9 import 'package:http/http.dart' as http; 9 import 'package:http/http.dart' as http;
10 import 'package:path/path.dart' as path; 10 import 'package:path/path.dart' as p;
11 import 'package:pub_semver/pub_semver.dart'; 11 import 'package:pub_semver/pub_semver.dart';
12 12
13 import '../exceptions.dart'; 13 import '../exceptions.dart';
14 import '../http.dart'; 14 import '../http.dart';
15 import '../io.dart'; 15 import '../io.dart';
16 import '../log.dart' as log; 16 import '../log.dart' as log;
17 import '../package.dart'; 17 import '../package.dart';
18 import '../pubspec.dart'; 18 import '../pubspec.dart';
19 import '../utils.dart'; 19 import '../utils.dart';
20 import 'cached.dart'; 20 import 'cached.dart';
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 107
108 return new Pubspec.fromMap( 108 return new Pubspec.fromMap(
109 version['pubspec'], systemCache.sources, 109 version['pubspec'], systemCache.sources,
110 expectedName: id.name, location: url); 110 expectedName: id.name, location: url);
111 } 111 }
112 112
113 /// Downloads the package identified by [id] to the system cache. 113 /// Downloads the package identified by [id] to the system cache.
114 Future<Package> downloadToSystemCache(PackageId id) async { 114 Future<Package> downloadToSystemCache(PackageId id) async {
115 if (!isInSystemCache(id)) { 115 if (!isInSystemCache(id)) {
116 var packageDir = getDirectory(id); 116 var packageDir = getDirectory(id);
117 ensureDir(path.dirname(packageDir)); 117 ensureDir(p.dirname(packageDir));
118 var parsed = _parseDescription(id.description); 118 var parsed = _parseDescription(id.description);
119 await _download(parsed.last, parsed.first, id.version, packageDir); 119 await _download(parsed.last, parsed.first, id.version, packageDir);
120 } 120 }
121 121
122 return new Package.load(id.name, getDirectory(id), systemCache.sources); 122 return new Package.load(id.name, getDirectory(id), systemCache.sources);
123 } 123 }
124 124
125 /// The system cache directory for the hosted source contains subdirectories 125 /// The system cache directory for the hosted source contains subdirectories
126 /// for each separate repository URL that's used on the system. 126 /// for each separate repository URL that's used on the system.
127 /// 127 ///
128 /// Each of these subdirectories then contains a subdirectory for each 128 /// Each of these subdirectories then contains a subdirectory for each
129 /// package downloaded from that site. 129 /// package downloaded from that site.
130 String getDirectory(PackageId id) { 130 String getDirectory(PackageId id) {
131 var parsed = _parseDescription(id.description); 131 var parsed = _parseDescription(id.description);
132 var dir = _urlToDirectory(parsed.last); 132 var dir = _urlToDirectory(parsed.last);
133 return path.join(systemCacheRoot, dir, "${parsed.first}-${id.version}"); 133 return p.join(systemCacheRoot, dir, "${parsed.first}-${id.version}");
134 } 134 }
135 135
136 String packageName(description) => _parseDescription(description).first; 136 String packageName(description) => _parseDescription(description).first;
137 137
138 bool descriptionsEqual(description1, description2) => 138 bool descriptionsEqual(description1, description2) =>
139 _parseDescription(description1) == _parseDescription(description2); 139 _parseDescription(description1) == _parseDescription(description2);
140 140
141 /// Ensures that [description] is a valid hosted package description. 141 /// Ensures that [description] is a valid hosted package description.
142 /// 142 ///
143 /// There are two valid formats. A plain string refers to a package with the 143 /// There are two valid formats. A plain string refers to a package with the
(...skipping 11 matching lines...) Expand all
155 155
156 /// Re-downloads all packages that have been previously downloaded into the 156 /// Re-downloads all packages that have been previously downloaded into the
157 /// system cache from any server. 157 /// system cache from any server.
158 Future<Pair<List<PackageId>, List<PackageId>>> repairCachedPackages() async { 158 Future<Pair<List<PackageId>, List<PackageId>>> repairCachedPackages() async {
159 if (!dirExists(systemCacheRoot)) return new Pair([], []); 159 if (!dirExists(systemCacheRoot)) return new Pair([], []);
160 160
161 var successes = []; 161 var successes = [];
162 var failures = []; 162 var failures = [];
163 163
164 for (var serverDir in listDir(systemCacheRoot)) { 164 for (var serverDir in listDir(systemCacheRoot)) {
165 var url = _directoryToUrl(path.basename(serverDir)); 165 var url = _directoryToUrl(p.basename(serverDir));
166 var packages = _getCachedPackagesInDirectory(path.basename(serverDir)); 166 var packages = _getCachedPackagesInDirectory(p.basename(serverDir));
167 packages.sort(Package.orderByNameAndVersion); 167 packages.sort(Package.orderByNameAndVersion);
168 168
169 for (var package in packages) { 169 for (var package in packages) {
170 var id = idFor(package.name, package.version, url: url); 170 var id = idFor(package.name, package.version, url: url);
171 171
172 try { 172 try {
173 await _download(url, package.name, package.version, package.dir); 173 await _download(url, package.name, package.version, package.dir);
174 successes.add(id); 174 successes.add(id);
175 } catch (error, stackTrace) { 175 } catch (error, stackTrace) {
176 failures.add(id); 176 failures.add(id);
(...skipping 13 matching lines...) Expand all
190 190
191 /// Gets all of the packages that have been downloaded into the system cache 191 /// Gets all of the packages that have been downloaded into the system cache
192 /// from the default server. 192 /// from the default server.
193 List<Package> getCachedPackages() { 193 List<Package> getCachedPackages() {
194 return _getCachedPackagesInDirectory(_urlToDirectory(defaultUrl)); 194 return _getCachedPackagesInDirectory(_urlToDirectory(defaultUrl));
195 } 195 }
196 196
197 /// Gets all of the packages that have been downloaded into the system cache 197 /// Gets all of the packages that have been downloaded into the system cache
198 /// into [dir]. 198 /// into [dir].
199 List<Package> _getCachedPackagesInDirectory(String dir) { 199 List<Package> _getCachedPackagesInDirectory(String dir) {
200 var cacheDir = path.join(systemCacheRoot, dir); 200 var cacheDir = p.join(systemCacheRoot, dir);
201 if (!dirExists(cacheDir)) return []; 201 if (!dirExists(cacheDir)) return [];
202 202
203 return listDir(cacheDir) 203 return listDir(cacheDir)
204 .map((entry) => new Package.load(null, entry, systemCache.sources)) 204 .map((entry) => new Package.load(null, entry, systemCache.sources))
205 .toList(); 205 .toList();
206 } 206 }
207 207
208 /// Downloads package [package] at [version] from [server], and unpacks it 208 /// Downloads package [package] at [version] from [server], and unpacks it
209 /// into [destPath]. 209 /// into [destPath].
210 Future _download(String server, String package, Version version, 210 Future _download(String server, String package, Version version,
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 /// This uses the system cache to get the list of available packages and does 256 /// This uses the system cache to get the list of available packages and does
257 /// no network access. 257 /// no network access.
258 class OfflineHostedSource extends HostedSource { 258 class OfflineHostedSource extends HostedSource {
259 /// Gets the list of all versions of [ref] that are in the system cache. 259 /// Gets the list of all versions of [ref] that are in the system cache.
260 Future<List<PackageId>> doGetVersions(PackageRef ref) async { 260 Future<List<PackageId>> doGetVersions(PackageRef ref) async {
261 var parsed = _parseDescription(ref.description); 261 var parsed = _parseDescription(ref.description);
262 var server = parsed.last; 262 var server = parsed.last;
263 log.io("Finding versions of ${ref.name} in " 263 log.io("Finding versions of ${ref.name} in "
264 "$systemCacheRoot/${_urlToDirectory(server)}"); 264 "$systemCacheRoot/${_urlToDirectory(server)}");
265 265
266 var dir = path.join(systemCacheRoot, _urlToDirectory(server)); 266 var dir = p.join(systemCacheRoot, _urlToDirectory(server));
267 267
268 var versions; 268 var versions;
269 if (dirExists(dir)) { 269 if (dirExists(dir)) {
270 versions = await listDir(dir).map((entry) { 270 versions = await listDir(dir).map((entry) {
271 var components = path.basename(entry).split("-"); 271 var components = p.basename(entry).split("-");
272 if (components.first != ref.name) return null; 272 if (components.first != ref.name) return null;
273 return HostedSource.idFor( 273 return HostedSource.idFor(
274 ref.name, new Version.parse(components.skip(1).join("-")), 274 ref.name, new Version.parse(components.skip(1).join("-")),
275 url: _serverFor(ref.description)); 275 url: _serverFor(ref.description));
276 }).where((id) => id != null).toList(); 276 }).where((id) => id != null).toList();
277 } else { 277 } else {
278 versions = []; 278 versions = [];
279 } 279 }
280 280
281 // If there are no versions in the cache, report a clearer error. 281 // If there are no versions in the cache, report a clearer error.
(...skipping 26 matching lines...) Expand all
308 /// incorrectly: it uses the character's *decimal* ASCII value instead of hex. 308 /// incorrectly: it uses the character's *decimal* ASCII value instead of hex.
309 /// 309 ///
310 /// This could cause an ambiguity since some characters get encoded as three 310 /// This could cause an ambiguity since some characters get encoded as three
311 /// digits and others two. It's possible for one to be a prefix of the other. 311 /// digits and others two. It's possible for one to be a prefix of the other.
312 /// In practice, the set of characters that are encoded don't happen to have 312 /// In practice, the set of characters that are encoded don't happen to have
313 /// any collisions, so the encoding is reversible. 313 /// any collisions, so the encoding is reversible.
314 /// 314 ///
315 /// This behavior is a bug, but is being preserved for compatibility. 315 /// This behavior is a bug, but is being preserved for compatibility.
316 String _urlToDirectory(String url) { 316 String _urlToDirectory(String url) {
317 // Normalize all loopback URLs to "localhost". 317 // Normalize all loopback URLs to "localhost".
318 url = url.replaceAllMapped(new RegExp(r"^https?://(127\.0\.0\.1|\[::1\])?"), 318 url = url.replaceAllMapped(
319 (match) => match[1] == null ? '' : 'localhost'); 319 new RegExp(r"^(https?://)(127\.0\.0\.1|\[::1\]|localhost)?"),
320 (match) {
321 // Don't include the scheme for HTTPS URLs. This makes the directory names
322 // nice for the default and most recommended scheme. We also don't include
323 // it for localhost URLs, since they're always known to be HTTP.
324 var localhost = match[2] == null ? '' : 'localhost';
325 var scheme = match[1] == 'https://' || localhost.isNotEmpty ? '' : match[1];
326 return "$scheme$localhost";
327 });
320 return replace(url, new RegExp(r'[<>:"\\/|?*%]'), 328 return replace(url, new RegExp(r'[<>:"\\/|?*%]'),
321 (match) => '%${match[0].codeUnitAt(0)}'); 329 (match) => '%${match[0].codeUnitAt(0)}');
322 } 330 }
323 331
324 /// Given a directory name in the system cache, returns the URL of the server 332 /// Given a directory name in the system cache, returns the URL of the server
325 /// whose packages it contains. 333 /// whose packages it contains.
326 /// 334 ///
327 /// See [_urlToDirectory] for details on the mapping. Note that because the 335 /// See [_urlToDirectory] for details on the mapping. Note that because the
328 /// directory name does not preserve the scheme, this has to guess at it. It 336 /// directory name does not preserve the scheme, this has to guess at it. It
329 /// chooses "http" for loopback URLs (mainly to support the pub tests) and 337 /// chooses "http" for loopback URLs (mainly to support the pub tests) and
330 /// "https" for all others. 338 /// "https" for all others.
331 String _directoryToUrl(String url) { 339 String _directoryToUrl(String url) {
332 // Decode the pseudo-URL-encoded characters. 340 // Decode the pseudo-URL-encoded characters.
333 var chars = '<>:"\\/|?*%'; 341 var chars = '<>:"\\/|?*%';
334 for (var i = 0; i < chars.length; i++) { 342 for (var i = 0; i < chars.length; i++) {
335 var c = chars.substring(i, i + 1); 343 var c = chars.substring(i, i + 1);
336 url = url.replaceAll("%${c.codeUnitAt(0)}", c); 344 url = url.replaceAll("%${c.codeUnitAt(0)}", c);
337 } 345 }
338 346
339 // Figure out the scheme. 347 // If the URL has an explicit scheme, use that.
340 var scheme = "https"; 348 if (url.contains("://")) return url;
341 349
342 // See if it's a loopback IP address. 350 // Otherwise, default to http for localhost and https for everything else.
343 if (isLoopback(url.replaceAll(new RegExp(":.*"), ""))) scheme = "http"; 351 var scheme =
352 isLoopback(url.replaceAll(new RegExp(":.*"), "")) ? "http" : "https";
344 return "$scheme://$url"; 353 return "$scheme://$url";
345 } 354 }
346 355
347 /// Parses [description] into its server and package name components, then 356 /// Parses [description] into its server and package name components, then
348 /// converts that to a Uri given [pattern]. 357 /// converts that to a Uri given [pattern].
349 /// 358 ///
350 /// Ensures the package name is properly URL encoded. 359 /// Ensures the package name is properly URL encoded.
351 Uri _makeUrl(description, String pattern(String server, String package)) { 360 Uri _makeUrl(description, String pattern(String server, String package)) {
352 var parsed = _parseDescription(description); 361 var parsed = _parseDescription(description);
353 var server = parsed.last; 362 var server = parsed.last;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 var name = description["name"]; 402 var name = description["name"];
394 if (name is! String) { 403 if (name is! String) {
395 throw new FormatException("The 'name' key must have a string value."); 404 throw new FormatException("The 'name' key must have a string value.");
396 } 405 }
397 406
398 var url = description["url"]; 407 var url = description["url"];
399 if (url == null) url = HostedSource.defaultUrl; 408 if (url == null) url = HostedSource.defaultUrl;
400 409
401 return new Pair<String, String>(name, url); 410 return new Pair<String, String>(name, url);
402 } 411 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698