-
Notifications
You must be signed in to change notification settings - Fork 160
Limit file count and total length in pkg/pub_dartdoc. #4765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'dart:convert'; | ||
import 'dart:io' as io; | ||
|
||
import 'package:analyzer/dart/element/element.dart'; | ||
import 'package:analyzer/file_system/file_system.dart'; | ||
import 'package:analyzer/file_system/memory_file_system.dart'; | ||
// ignore: implementation_imports | ||
import 'package:analyzer/src/generated/sdk.dart'; | ||
// ignore: implementation_imports | ||
import 'package:analyzer/src/generated/source.dart'; | ||
import 'package:dartdoc/dartdoc.dart'; | ||
import 'package:path/path.dart' as p; | ||
import 'package:watcher/watcher.dart'; | ||
|
||
const _maxFileCount = 10000; | ||
const _maxTotalLengthBytes = 10 * 1024 * 1024; | ||
|
||
/// Creates an overlay file system with binary file support on top | ||
/// of the input sources. | ||
/// | ||
/// TODO: Use a propr overlay in-memory filesystem with binary support, | ||
/// instead of overriding file writes in the output path. | ||
class PubResourceProvider implements ResourceProvider { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just create a proper overlay, instead of relying on output folder detection? When writing we always write to I guess the complicated part is when listing folders, we'll need to wrap and merge them... But it would be less magic.. We can also leave that as a TODO... (I'm guessing we can almost just steal the overlay implementation from analyzer and binary file support to it, why not? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ++ |
||
final ResourceProvider _defaultProvider; | ||
final _memoryResourceProvider = MemoryResourceProvider(); | ||
String _outputPath; | ||
int _fileCount = 0; | ||
int _totalLengthBytes = 0; | ||
|
||
PubResourceProvider(this._defaultProvider); | ||
|
||
/// Writes in-memory files to disk. | ||
void writeFilesToDiskSync() { | ||
void storeFolder(Folder rs) { | ||
for (final c in rs.getChildren()) { | ||
if (c is Folder) { | ||
storeFolder(c); | ||
} else if (c is File) { | ||
final file = io.File(c.path); | ||
file.parent.createSync(recursive: true); | ||
file.writeAsBytes(c.readAsBytesSync()); | ||
} | ||
} | ||
} | ||
|
||
storeFolder(_memoryResourceProvider.getFolder(_outputPath)); | ||
} | ||
|
||
/// Checks if we have reached any file write limit before storing the bytes. | ||
void _aboutToWriteBytes(int length) { | ||
_fileCount++; | ||
_totalLengthBytes += length; | ||
if (_fileCount > _maxFileCount) { | ||
throw AssertionError( | ||
'Reached $_maxFileCount files in the output directory.'); | ||
} | ||
if (_totalLengthBytes > _maxTotalLengthBytes) { | ||
throw AssertionError( | ||
'Reached $_maxTotalLengthBytes bytes in the output directory.'); | ||
} | ||
} | ||
|
||
void setOutput(String value) { | ||
_outputPath = value; | ||
} | ||
|
||
bool _isOutput(String path) { | ||
return _outputPath != null && | ||
(path == _outputPath || p.isWithin(_outputPath, path)); | ||
} | ||
|
||
ResourceProvider _rp(String path) => | ||
_isOutput(path) ? _memoryResourceProvider : _defaultProvider; | ||
|
||
@override | ||
File getFile(String path) => _File(this, _rp(path).getFile(path)); | ||
|
||
@override | ||
Folder getFolder(String path) => _rp(path).getFolder(path); | ||
|
||
@override | ||
Future<List<int>> getModificationTimes(List<Source> sources) async { | ||
return _defaultProvider.getModificationTimes(sources); | ||
} | ||
|
||
@override | ||
Resource getResource(String path) => _rp(path).getResource(path); | ||
|
||
@override | ||
Folder getStateLocation(String pluginId) { | ||
return _defaultProvider.getStateLocation(pluginId); | ||
} | ||
|
||
@override | ||
p.Context get pathContext => _defaultProvider.pathContext; | ||
} | ||
|
||
class _File implements File { | ||
final PubResourceProvider _provider; | ||
final File _delegate; | ||
_File(this._provider, this._delegate); | ||
|
||
@override | ||
Stream<WatchEvent> get changes => _delegate.changes; | ||
|
||
@override | ||
File copyTo(Folder parentFolder) => _delegate.copyTo(parentFolder); | ||
|
||
@override | ||
Source createSource([Uri uri]) => _delegate.createSource(uri); | ||
|
||
@override | ||
void delete() => _delegate.delete(); | ||
|
||
@override | ||
bool get exists => _delegate.exists; | ||
|
||
@override | ||
bool isOrContains(String path) => _delegate.isOrContains(path); | ||
|
||
@override | ||
int get lengthSync => _delegate.lengthSync; | ||
|
||
@override | ||
int get modificationStamp => _delegate.modificationStamp; | ||
|
||
@override | ||
Folder get parent => _delegate.parent2; | ||
|
||
@override | ||
Folder get parent2 => _delegate.parent2; | ||
|
||
@override | ||
String get path => _delegate.path; | ||
|
||
@override | ||
ResourceProvider get provider => _delegate.provider; | ||
|
||
@override | ||
List<int> readAsBytesSync() => _delegate.readAsBytesSync(); | ||
|
||
@override | ||
String readAsStringSync() => _delegate.readAsStringSync(); | ||
|
||
@override | ||
File renameSync(String newPath) => _delegate.renameSync(newPath); | ||
|
||
@override | ||
Resource resolveSymbolicLinksSync() => _delegate.resolveSymbolicLinksSync(); | ||
|
||
@override | ||
String get shortName => _delegate.shortName; | ||
|
||
@override | ||
Uri toUri() => _delegate.toUri(); | ||
|
||
@override | ||
void writeAsBytesSync(List<int> bytes) { | ||
_provider._aboutToWriteBytes(bytes.length); | ||
_delegate.writeAsBytesSync(bytes); | ||
} | ||
|
||
@override | ||
void writeAsStringSync(String content) { | ||
writeAsBytesSync(utf8.encode(content)); | ||
} | ||
} | ||
|
||
/// Allows the override of [resourceProvider]. | ||
class PubPackageMetaProvider implements PackageMetaProvider { | ||
final PackageMetaProvider _delegate; | ||
final ResourceProvider _resourceProvider; | ||
|
||
PubPackageMetaProvider(this._delegate, this._resourceProvider); | ||
|
||
@override | ||
DartSdk get defaultSdk => _delegate.defaultSdk; | ||
|
||
@override | ||
Folder get defaultSdkDir => _delegate.defaultSdkDir; | ||
|
||
@override | ||
PackageMeta fromDir(Folder dir) => _delegate.fromDir(dir); | ||
|
||
@override | ||
PackageMeta fromElement(LibraryElement library, String s) => | ||
_delegate.fromElement(library, s); | ||
|
||
@override | ||
PackageMeta fromFilename(String s) => _delegate.fromFilename(s); | ||
|
||
@override | ||
ResourceProvider get resourceProvider => _resourceProvider; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:analyzer/file_system/memory_file_system.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import 'package:pub_dartdoc/src/pub_hooks.dart'; | ||
|
||
void main() { | ||
test('limit number of files', () { | ||
final provider = PubResourceProvider(MemoryResourceProvider()); | ||
|
||
for (var i = 0; i < 10000; i++) { | ||
provider.getFile('/tmp/$i.txt').writeAsStringSync('x'); | ||
} | ||
|
||
expect(() => provider.getFile('/tmp/next.txt').writeAsStringSync('next'), | ||
throwsA(isA<AssertionError>())); | ||
}); | ||
|
||
test('limit total bytes', () { | ||
final provider = PubResourceProvider(MemoryResourceProvider()); | ||
provider | ||
.getFile('/tmp/1') | ||
.writeAsBytesSync(List<int>.filled(10 * 1024 * 1024, 0)); | ||
expect(() => provider.getFile('/tmp/2').writeAsBytesSync(<int>[0]), | ||
throwsA(isA<AssertionError>())); | ||
}); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.