Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
4c47fdad
Unverified
Commit
4c47fdad
authored
5 years ago
by
Jonah Williams
Committed by
GitHub
5 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add devfs for incremental compiler JavaScript bundle (#43219)
parent
ed931e79
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
427 additions
and
0 deletions
+427
-0
io.dart
packages/flutter_tools/lib/src/base/io.dart
+1
-0
devfs_web.dart
packages/flutter_tools/lib/src/web/devfs_web.dart
+160
-0
devfs_web_test.dart
.../flutter_tools/test/general.shard/web/devfs_web_test.dart
+266
-0
No files found.
packages/flutter_tools/lib/src/base/io.dart
View file @
4c47fdad
...
...
@@ -52,6 +52,7 @@ export 'dart:io'
HttpException
,
HttpHeaders
,
HttpRequest
,
HttpResponse
,
HttpServer
,
HttpStatus
,
InternetAddress
,
...
...
This diff is collapsed.
Click to expand it.
packages/flutter_tools/lib/src/web/devfs_web.dart
0 → 100644
View file @
4c47fdad
// Copyright 2019 The Chromium Authors. 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:typed_data'
;
import
'package:meta/meta.dart'
;
import
'package:mime/mime.dart'
as
mime
;
import
'../base/common.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../build_info.dart'
;
import
'../convert.dart'
;
import
'../globals.dart'
;
/// A web server which handles serving JavaScript and assets.
///
/// This is only used in development mode.
class
WebAssetServer
{
@visibleForTesting
WebAssetServer
(
this
.
_httpServer
,
{
@required
void
Function
(
dynamic
,
StackTrace
)
onError
})
{
_httpServer
.
listen
((
HttpRequest
request
)
{
_handleRequest
(
request
).
catchError
(
onError
);
// TODO(jonahwilliams): test the onError callback when https://github.com/dart-lang/sdk/issues/39094 is fixed.
},
onError:
onError
);
}
// Fallback to "application/octet-stream" on null which
// makes no claims as to the structure of the data.
static
const
String
_kDefaultMimeType
=
'application/octet-stream'
;
/// Start the web asset server on a [hostname] and [port].
///
/// Unhandled exceptions will throw a [ToolExit] with the error and stack
/// trace.
static
Future
<
WebAssetServer
>
start
(
String
hostname
,
int
port
)
async
{
try
{
final
HttpServer
httpServer
=
await
HttpServer
.
bind
(
hostname
,
port
);
return
WebAssetServer
(
httpServer
,
onError:
(
dynamic
error
,
StackTrace
stackTrace
)
{
httpServer
.
close
(
force:
true
);
throwToolExit
(
'Unhandled exception in web development server:
\n
$error
\n
$stackTrace
'
);
});
}
on
SocketException
catch
(
err
)
{
throwToolExit
(
'Failed to bind web development server:
\n
$err
'
);
}
assert
(
false
);
return
null
;
}
final
HttpServer
_httpServer
;
final
Map
<
String
,
Uint8List
>
_files
=
<
String
,
Uint8List
>{};
// handle requests for JavaScript source, dart sources maps, or asset files.
Future
<
void
>
_handleRequest
(
HttpRequest
request
)
async
{
final
HttpResponse
response
=
request
.
response
;
// If the response is `/`, then we are requesting the index file.
if
(
request
.
uri
.
path
==
'/'
)
{
final
File
indexFile
=
fs
.
currentDirectory
.
childDirectory
(
'web'
)
.
childFile
(
'index.html'
);
if
(
indexFile
.
existsSync
())
{
response
.
headers
.
add
(
'Content-Type'
,
'text/html'
);
response
.
headers
.
add
(
'Content-Length'
,
indexFile
.
lengthSync
());
await
response
.
addStream
(
indexFile
.
openRead
());
}
else
{
response
.
statusCode
=
HttpStatus
.
notFound
;
}
await
response
.
close
();
return
;
}
// If this is a JavaScript file, it must be in the in-memory cache.
// Attempt to look up the file by URI, returning a 404 if it is not
// found.
if
(
_files
.
containsKey
(
request
.
uri
.
path
))
{
final
List
<
int
>
bytes
=
_files
[
request
.
uri
.
path
];
response
.
headers
..
add
(
'Content-Length'
,
bytes
.
length
)
..
add
(
'Content-Type'
,
'application/javascript'
);
response
.
add
(
bytes
);
await
response
.
close
();
return
;
}
// If this is a dart file, it must be on the local file system and is
// likely coming from a source map request. Attempt to look in the
// local filesystem for it, and return a 404 if it is not found. The tool
// doesn't currently consider the case of Dart files as assets.
File
file
=
fs
.
file
(
Uri
.
base
.
resolve
(
request
.
uri
.
path
));
// If both of the lookups above failed, the file might have been an asset.
// Try and resolve the path relative to the built asset directory.
if
(!
file
.
existsSync
())
{
final
String
assetPath
=
request
.
uri
.
path
.
replaceFirst
(
'/assets/'
,
''
);
file
=
fs
.
file
(
fs
.
path
.
join
(
getAssetBuildDirectory
(),
fs
.
path
.
relative
(
assetPath
)));
}
if
(!
file
.
existsSync
())
{
response
.
statusCode
=
HttpStatus
.
notFound
;
await
response
.
close
();
return
;
}
final
int
length
=
file
.
lengthSync
();
// Attempt to determine the file's mime type. if this is not provided some
// browsers will refuse to render images/show video et cetera. If the tool
// cannot determine a mime type, fall back to application/octet-stream.
String
mimeType
;
if
(
length
>=
12
)
{
mimeType
=
mime
.
lookupMimeType
(
file
.
path
,
headerBytes:
await
file
.
openRead
(
0
,
12
).
first
,
);
}
mimeType
??=
_kDefaultMimeType
;
response
.
headers
.
add
(
'Content-Length'
,
length
);
response
.
headers
.
add
(
'Content-Type'
,
mimeType
);
await
response
.
addStream
(
file
.
openRead
());
await
response
.
close
();
}
/// Tear down the http server running.
Future
<
void
>
dispose
()
{
return
_httpServer
.
close
();
}
/// Write a single file into the in-memory cache.
void
writeFile
(
String
filePath
,
String
contents
)
{
_files
[
filePath
]
=
Uint8List
.
fromList
(
utf8
.
encode
(
contents
));
}
/// Update the in-memory asset server with the provided source and manifest files.
///
/// Returns a list of updated modules.
List
<
String
>
write
(
File
sourceFile
,
File
manifestFile
)
{
final
List
<
String
>
modules
=
<
String
>[];
final
Uint8List
bytes
=
sourceFile
.
readAsBytesSync
();
final
Map
<
String
,
Object
>
manifest
=
json
.
decode
(
manifestFile
.
readAsStringSync
());
for
(
String
filePath
in
manifest
.
keys
)
{
if
(
filePath
==
null
)
{
printTrace
(
'Invalid manfiest file:
$filePath
'
);
continue
;
}
final
List
<
Object
>
offsets
=
manifest
[
filePath
];
if
(
offsets
.
length
!=
2
)
{
printTrace
(
'Invalid manifest byte offsets:
$offsets
'
);
continue
;
}
final
int
start
=
offsets
[
0
];
final
int
end
=
offsets
[
1
];
if
(
start
<
0
||
end
>
bytes
.
lengthInBytes
)
{
printTrace
(
'Invalid byte index: [
$start
,
$end
]'
);
continue
;
}
final
Uint8List
byteView
=
Uint8List
.
view
(
bytes
.
buffer
,
start
,
end
-
start
);
_files
[
filePath
]
=
byteView
;
modules
.
add
(
filePath
);
}
return
modules
;
}
}
This diff is collapsed.
Click to expand it.
packages/flutter_tools/test/general.shard/web/devfs_web_test.dart
0 → 100644
View file @
4c47fdad
// Copyright 2019 The Chromium Authors. 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:async'
;
import
'dart:io'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/convert.dart'
;
import
'package:flutter_tools/src/web/devfs_web.dart'
;
import
'package:mockito/mockito.dart'
;
import
'../../src/common.dart'
;
import
'../../src/testbed.dart'
;
const
List
<
int
>
kTransparentImage
=
<
int
>[
0x89
,
0x50
,
0x4E
,
0x47
,
0x0D
,
0x0A
,
0x1A
,
0x0A
,
0x00
,
0x00
,
0x00
,
0x0D
,
0x49
,
0x48
,
0x44
,
0x52
,
0x00
,
0x00
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0x01
,
0x08
,
0x06
,
0x00
,
0x00
,
0x00
,
0x1F
,
0x15
,
0xC4
,
0x89
,
0x00
,
0x00
,
0x00
,
0x0A
,
0x49
,
0x44
,
0x41
,
0x54
,
0x78
,
0x9C
,
0x63
,
0x00
,
0x01
,
0x00
,
0x00
,
0x05
,
0x00
,
0x01
,
0x0D
,
0x0A
,
0x2D
,
0xB4
,
0x00
,
0x00
,
0x00
,
0x00
,
0x49
,
0x45
,
0x4E
,
0x44
,
0xAE
,
];
void
main
(
)
{
MockHttpServer
mockHttpServer
;
StreamController
<
HttpRequest
>
requestController
;
Testbed
testbed
;
MockHttpRequest
request
;
MockHttpResponse
response
;
MockHttpHeaders
headers
;
Completer
<
void
>
closeCompleter
;
WebAssetServer
webAssetServer
;
MockPlatform
windows
;
MockPlatform
linux
;
setUp
(()
{
windows
=
MockPlatform
();
linux
=
MockPlatform
();
when
(
windows
.
environment
).
thenReturn
(
const
<
String
,
String
>{});
when
(
windows
.
isWindows
).
thenReturn
(
true
);
when
(
linux
.
isWindows
).
thenReturn
(
false
);
when
(
linux
.
environment
).
thenReturn
(
const
<
String
,
String
>{});
testbed
=
Testbed
(
setup:
()
{
mockHttpServer
=
MockHttpServer
();
requestController
=
StreamController
<
HttpRequest
>.
broadcast
();
request
=
MockHttpRequest
();
response
=
MockHttpResponse
();
headers
=
MockHttpHeaders
();
closeCompleter
=
Completer
<
void
>();
when
(
mockHttpServer
.
listen
(
any
,
onError:
anyNamed
(
'onError'
))).
thenAnswer
((
Invocation
invocation
)
{
final
Function
callback
=
invocation
.
positionalArguments
.
first
;
return
requestController
.
stream
.
listen
(
callback
);
});
when
(
request
.
response
).
thenReturn
(
response
);
when
(
response
.
headers
).
thenReturn
(
headers
);
when
(
response
.
close
()).
thenAnswer
((
Invocation
invocation
)
async
{
closeCompleter
.
complete
();
});
webAssetServer
=
WebAssetServer
(
mockHttpServer
,
onError:
(
dynamic
error
,
StackTrace
stackTrace
)
{
closeCompleter
.
completeError
(
error
,
stackTrace
);
});
});
});
tearDown
(()
async
{
await
webAssetServer
.
dispose
();
await
requestController
.
close
();
});
test
(
'Throws a tool exit if bind fails with a SocketException'
,
()
=>
testbed
.
run
(()
async
{
expect
(
WebAssetServer
.
start
(
'hello'
,
1234
),
throwsA
(
isInstanceOf
<
ToolExit
>()));
}));
test
(
'Can catch exceptions through the onError callback'
,
()
=>
testbed
.
run
(()
async
{
when
(
response
.
close
()).
thenAnswer
((
Invocation
invocation
)
{
throw
StateError
(
'Something bad'
);
});
webAssetServer
.
writeFile
(
'/foo.js'
,
'main() {}'
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/foo.js'
));
requestController
.
add
(
request
);
expect
(
closeCompleter
.
future
,
throwsA
(
isInstanceOf
<
StateError
>()));
}));
test
(
'Handles against malformed manifest'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
'source'
)
..
writeAsStringSync
(
'main() {}'
);
// Missing ending offset.
final
File
manifestMissingOffset
=
fs
.
file
(
'manifestA'
)
..
writeAsStringSync
(
json
.
encode
(<
String
,
Object
>{
'/foo.js'
:
<
int
>[
0
]}));
// Non-file URI.
final
File
manifestNonFileScheme
=
fs
.
file
(
'manifestA'
)
..
writeAsStringSync
(
json
.
encode
(<
String
,
Object
>{
'/foo.js'
:
<
int
>[
0
,
10
]}));
final
File
manifestOutOfBounds
=
fs
.
file
(
'manifest'
)
..
writeAsStringSync
(
json
.
encode
(<
String
,
Object
>{
'/foo.js'
:
<
int
>[
0
,
100
]}));
expect
(
webAssetServer
.
write
(
source
,
manifestMissingOffset
),
isEmpty
);
expect
(
webAssetServer
.
write
(
source
,
manifestNonFileScheme
),
isEmpty
);
expect
(
webAssetServer
.
write
(
source
,
manifestOutOfBounds
),
isEmpty
);
}));
test
(
'serves JavaScript files from in memory cache'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
'source'
)
..
writeAsStringSync
(
'main() {}'
);
final
File
manifest
=
fs
.
file
(
'manifest'
)
..
writeAsStringSync
(
json
.
encode
(<
String
,
Object
>{
'/foo.js'
:
<
int
>[
0
,
source
.
lengthSync
()]}));
webAssetServer
.
write
(
source
,
manifest
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/foo.js'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
source
.
lengthSync
())).
called
(
1
);
verify
(
headers
.
add
(
'Content-Type'
,
'application/javascript'
)).
called
(
1
);
verify
(
response
.
add
(
source
.
readAsBytesSync
())).
called
(
1
);
}));
test
(
'serves JavaScript files from in memory cache not from manifest'
,
()
=>
testbed
.
run
(()
async
{
webAssetServer
.
writeFile
(
'/foo.js'
,
'main() {}'
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/foo.js'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
9
)).
called
(
1
);
verify
(
headers
.
add
(
'Content-Type'
,
'application/javascript'
)).
called
(
1
);
verify
(
response
.
add
(
any
)).
called
(
1
);
}));
test
(
'handles missing JavaScript files from in memory cache'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
'source'
)
..
writeAsStringSync
(
'main() {}'
);
final
File
manifest
=
fs
.
file
(
'manifest'
)
..
writeAsStringSync
(
json
.
encode
(<
String
,
Object
>{
'/foo.js'
:
<
int
>[
0
,
source
.
lengthSync
()]}));
webAssetServer
.
write
(
source
,
manifest
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/bar.js'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
response
.
statusCode
=
404
).
called
(
1
);
}));
test
(
'serves Dart files from in filesystem on Windows'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
'foo.dart'
).
absolute
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
'void main() {}'
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/C:/foo.dart'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
source
.
lengthSync
())).
called
(
1
);
verify
(
response
.
addStream
(
any
)).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
Platform:
()
=>
windows
,
}));
test
(
'serves Dart files from in filesystem on Linux/macOS'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
'foo.dart'
).
absolute
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
'void main() {}'
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/foo.dart'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
source
.
lengthSync
())).
called
(
1
);
verify
(
response
.
addStream
(
any
)).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
Platform:
()
=>
linux
,
}));
test
(
'Handles missing Dart files from filesystem'
,
()
=>
testbed
.
run
(()
async
{
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/foo.dart'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
response
.
statusCode
=
404
).
called
(
1
);
}));
test
(
'serves asset files from in filesystem with known mime type'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
fs
.
path
.
join
(
'build'
,
'flutter_assets'
,
'foo.png'
))
..
createSync
(
recursive:
true
)
..
writeAsBytesSync
(
kTransparentImage
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/assets/foo.png'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
source
.
lengthSync
())).
called
(
1
);
verify
(
headers
.
add
(
'Content-Type'
,
'image/png'
)).
called
(
1
);
verify
(
response
.
addStream
(
any
)).
called
(
1
);
}));
test
(
'serves asset files from in filesystem with known mime type on Windows'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
fs
.
path
.
join
(
'build'
,
'flutter_assets'
,
'foo.png'
))
..
createSync
(
recursive:
true
)
..
writeAsBytesSync
(
kTransparentImage
);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/assets/foo.png'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
source
.
lengthSync
())).
called
(
1
);
verify
(
headers
.
add
(
'Content-Type'
,
'image/png'
)).
called
(
1
);
verify
(
response
.
addStream
(
any
)).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
Platform:
()
=>
windows
,
}));
test
(
'serves asset files files from in filesystem with unknown mime type and length > 12'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
fs
.
path
.
join
(
'build'
,
'flutter_assets'
,
'foo'
))
..
createSync
(
recursive:
true
)
..
writeAsBytesSync
(
List
<
int
>.
filled
(
100
,
0
));
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/assets/foo'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
source
.
lengthSync
())).
called
(
1
);
verify
(
headers
.
add
(
'Content-Type'
,
'application/octet-stream'
)).
called
(
1
);
verify
(
response
.
addStream
(
any
)).
called
(
1
);
}));
test
(
'serves asset files files from in filesystem with unknown mime type and length < 12'
,
()
=>
testbed
.
run
(()
async
{
final
File
source
=
fs
.
file
(
fs
.
path
.
join
(
'build'
,
'flutter_assets'
,
'foo'
))
..
createSync
(
recursive:
true
)
..
writeAsBytesSync
(<
int
>[
1
,
2
,
3
]);
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/assets/foo'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
headers
.
add
(
'Content-Length'
,
source
.
lengthSync
())).
called
(
1
);
verify
(
headers
.
add
(
'Content-Type'
,
'application/octet-stream'
)).
called
(
1
);
verify
(
response
.
addStream
(
any
)).
called
(
1
);
}));
test
(
'handles serving missing asset file'
,
()
=>
testbed
.
run
(()
async
{
when
(
request
.
uri
).
thenReturn
(
Uri
.
parse
(
'http://foobar/assets/foo'
));
requestController
.
add
(
request
);
await
closeCompleter
.
future
;
verify
(
response
.
statusCode
=
HttpStatus
.
notFound
).
called
(
1
);
}));
test
(
'calling dispose closes the http server'
,
()
=>
testbed
.
run
(()
async
{
await
webAssetServer
.
dispose
();
verify
(
mockHttpServer
.
close
()).
called
(
1
);
}));
}
class
MockHttpServer
extends
Mock
implements
HttpServer
{}
class
MockHttpRequest
extends
Mock
implements
HttpRequest
{}
class
MockHttpResponse
extends
Mock
implements
HttpResponse
{}
class
MockHttpHeaders
extends
Mock
implements
HttpHeaders
{}
class
MockPlatform
extends
Mock
implements
Platform
{}
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment