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
400585cb
Commit
400585cb
authored
Sep 20, 2016
by
Chinmay Garde
Committed by
GitHub
Sep 20, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update flutter/http.dart to use dart:io. (#5940)
parent
4f0eff31
Changes
25
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
1695 additions
and
477 deletions
+1695
-477
media_service.dart
examples/layers/services/media_service.dart
+5
-1
http.dart
packages/flutter/lib/http.dart
+8
-6
README.md
packages/flutter/lib/src/http/README.md
+7
-0
base_client.dart
packages/flutter/lib/src/http/base_client.dart
+199
-0
base_request.dart
packages/flutter/lib/src/http/base_request.dart
+141
-0
base_response.dart
packages/flutter/lib/src/http/base_response.dart
+53
-0
byte_stream.dart
packages/flutter/lib/src/http/byte_stream.dart
+36
-0
client.dart
packages/flutter/lib/src/http/client.dart
+148
-0
exception.dart
packages/flutter/lib/src/http/exception.dart
+16
-0
http.dart
packages/flutter/lib/src/http/http.dart
+76
-98
io.dart
packages/flutter/lib/src/http/io.dart
+26
-0
io_client.dart
packages/flutter/lib/src/http/io_client.dart
+90
-0
mock_client.dart
packages/flutter/lib/src/http/mock_client.dart
+88
-0
mojo_client.dart
packages/flutter/lib/src/http/mojo_client.dart
+0
-260
multipart_file.dart
packages/flutter/lib/src/http/multipart_file.dart
+111
-0
multipart_request.dart
packages/flutter/lib/src/http/multipart_request.dart
+177
-0
request.dart
packages/flutter/lib/src/http/request.dart
+164
-0
response.dart
packages/flutter/lib/src/http/response.dart
+76
-85
streamed_request.dart
packages/flutter/lib/src/http/streamed_request.dart
+42
-0
streamed_response.dart
packages/flutter/lib/src/http/streamed_response.dart
+39
-0
utils.dart
packages/flutter/lib/src/http/utils.dart
+143
-0
asset_bundle.dart
packages/flutter/lib/src/services/asset_bundle.dart
+8
-20
image_provider.dart
packages/flutter/lib/src/services/image_provider.dart
+29
-7
pubspec.yaml
packages/flutter/pubspec.yaml
+5
-0
binding.dart
packages/flutter_test/lib/src/binding.dart
+8
-0
No files found.
examples/layers/services/media_service.dart
View file @
400585cb
...
...
@@ -9,6 +9,7 @@ import 'package:flutter/material.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/http.dart'
as
http
;
import
'package:mojo/core.dart'
as
core
;
// All of these sounds are marked as public domain at soundbible.
const
String
chimes
=
"http://soundbible.com/grab.php?id=2030&type=wav"
;
...
...
@@ -42,7 +43,10 @@ class PianoKey {
Future
<
Null
>
load
(
mojom
.
MediaServiceProxy
mediaService
)
async
{
try
{
mediaService
.
createPlayer
(
player
);
player
.
prepare
(
await
http
.
readDataPipe
(
soundUrl
),
(
bool
ignored
)
{
});
http
.
Response
response
=
await
http
.
get
(
soundUrl
);
core
.
MojoDataPipe
pipe
=
new
core
.
MojoDataPipe
();
core
.
DataPipeFiller
.
fillHandle
(
pipe
.
producer
,
response
.
bodyBytes
.
buffer
.
asByteData
());
player
.
prepare
(
pipe
.
consumer
,
(
bool
ignored
)
{
});
}
catch
(
e
)
{
print
(
"Error: failed to load sound file
$soundUrl
"
);
player
.
close
();
...
...
packages/flutter/lib/http.dart
View file @
400585cb
...
...
@@ -3,16 +3,18 @@
// found in the LICENSE file.
/// A [Future]-based library for making HTTP requests.
///
///
/// To use, import `package:flutter/http.dart`.
///
/// This library is based on Dart's `http` package, but differs in that it
is a
///
`mojo`-based HTTP client and does
not have a dependency on mirrors.
/// This library is based on Dart's `http` package, but differs in that it
does
/// not have a dependency on mirrors.
///
/// This library depends only on core Dart libraries as well as the `mojo`,
/// `mojo_services`, and `sky_services` packages.
// TODO(chinmaygarde): The contents of `lib/src/http` will become redundant
// once https://github.com/dart-lang/http/issues/1 is fixed (removes the use
// of mirrors). Once that issue is addressed, we should get rid this directory
// and use `dart-lang/http` directly.
library
http
;
export
'src/http/http.dart'
;
export
'src/http/mojo_client.dart'
;
export
'src/http/response.dart'
;
export
'src/http/mock_client.dart'
;
packages/flutter/lib/src/http/README.md
0 → 100644
View file @
400585cb
`dart-lang/http` without Mirrors
================================
The contents of this will become redundant once
https://github.com/dart-lang/http/issues/1 is fixed (removes the use
of mirrors). Once that issue is addressed, we should get rid this directory
and use
`dart-lang/http`
directly.
packages/flutter/lib/src/http/base_client.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'dart:convert'
;
import
'dart:typed_data'
;
import
'package:collection/collection.dart'
;
import
'base_request.dart'
;
import
'client.dart'
;
import
'exception.dart'
;
import
'request.dart'
;
import
'response.dart'
;
import
'streamed_response.dart'
;
/// The abstract base class for an HTTP client. This is a mixin-style class;
/// subclasses only need to implement [send] and maybe [close], and then they
/// get various convenience methods for free.
abstract
class
BaseClient
implements
Client
{
/// Sends an HTTP HEAD request with the given headers to the given URL, which
/// can be a [Uri] or a [String].
///
/// For more fine-grained control over the request, use [send] instead.
@override
Future
<
Response
>
head
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
})
=>
_sendUnstreamed
(
"HEAD"
,
url
,
headers
);
/// Sends an HTTP GET request with the given headers to the given URL, which
/// can be a [Uri] or a [String].
///
/// For more fine-grained control over the request, use [send] instead.
@override
Future
<
Response
>
get
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
})
=>
_sendUnstreamed
(
"GET"
,
url
,
headers
);
/// Sends an HTTP POST request with the given headers and body to the given
/// URL, which can be a [Uri] or a [String].
///
/// [body] sets the body of the request. It can be a [String], a [List<int>]
/// or a [Map<String, String>]. If it's a String, it's encoded using
/// [encoding] and used as the body of the request. The content-type of the
/// request will default to "text/plain".
///
/// If [body] is a List, it's used as a list of bytes for the body of the
/// request.
///
/// If [body] is a Map, it's encoded as form fields using [encoding]. The
/// content-type of the request will be set to
/// `"application/x-www-form-urlencoded"`; this cannot be overridden.
///
/// [encoding] defaults to UTF-8.
///
/// For more fine-grained control over the request, use [send] instead.
@override
Future
<
Response
>
post
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
,
dynamic
body
,
Encoding
encoding
})
=>
_sendUnstreamed
(
"POST"
,
url
,
headers
,
body
,
encoding
);
/// Sends an HTTP PUT request with the given headers and body to the given
/// URL, which can be a [Uri] or a [String].
///
/// [body] sets the body of the request. It can be a [String], a [List<int>]
/// or a [Map<String, String>]. If it's a String, it's encoded using
/// [encoding] and used as the body of the request. The content-type of the
/// request will default to "text/plain".
///
/// If [body] is a List, it's used as a list of bytes for the body of the
/// request.
///
/// If [body] is a Map, it's encoded as form fields using [encoding]. The
/// content-type of the request will be set to
/// `"application/x-www-form-urlencoded"`; this cannot be overridden.
///
/// [encoding] defaults to UTF-8.
///
/// For more fine-grained control over the request, use [send] instead.
@override
Future
<
Response
>
put
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
,
dynamic
body
,
Encoding
encoding
})
=>
_sendUnstreamed
(
"PUT"
,
url
,
headers
,
body
,
encoding
);
/// Sends an HTTP PATCH request with the given headers and body to the given
/// URL, which can be a [Uri] or a [String].
///
/// [body] sets the body of the request. It can be a [String], a [List<int>]
/// or a [Map<String, String>]. If it's a String, it's encoded using
/// [encoding] and used as the body of the request. The content-type of the
/// request will default to "text/plain".
///
/// If [body] is a List, it's used as a list of bytes for the body of the
/// request.
///
/// If [body] is a Map, it's encoded as form fields using [encoding]. The
/// content-type of the request will be set to
/// `"application/x-www-form-urlencoded"`; this cannot be overridden.
///
/// [encoding] defaults to UTF-8.
///
/// For more fine-grained control over the request, use [send] instead.
@override
Future
<
Response
>
patch
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
,
dynamic
body
,
Encoding
encoding
})
=>
_sendUnstreamed
(
"PATCH"
,
url
,
headers
,
body
,
encoding
);
/// Sends an HTTP DELETE request with the given headers to the given URL,
/// which can be a [Uri] or a [String].
///
/// For more fine-grained control over the request, use [send] instead.
@override
Future
<
Response
>
delete
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
})
=>
_sendUnstreamed
(
"DELETE"
,
url
,
headers
);
/// Sends an HTTP GET request with the given headers to the given URL, which
/// can be a [Uri] or a [String], and returns a Future that completes to the
/// body of the response as a String.
///
/// The Future will emit a [ClientException] if the response doesn't have a
/// success status code.
///
/// For more fine-grained control over the request and response, use [send] or
/// [get] instead.
@override
Future
<
String
>
read
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
})
{
return
get
(
url
,
headers:
headers
).
then
((
Response
response
)
{
_checkResponseSuccess
(
url
,
response
);
return
response
.
body
;
});
}
/// Sends an HTTP GET request with the given headers to the given URL, which
/// can be a [Uri] or a [String], and returns a Future that completes to the
/// body of the response as a list of bytes.
///
/// The Future will emit an [ClientException] if the response doesn't have a
/// success status code.
///
/// For more fine-grained control over the request and response, use [send] or
/// [get] instead.
@override
Future
<
Uint8List
>
readBytes
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
})
{
return
get
(
url
,
headers:
headers
).
then
((
Response
response
)
{
_checkResponseSuccess
(
url
,
response
);
return
response
.
bodyBytes
;
});
}
/// Sends an HTTP request and asynchronously returns the response.
///
/// Implementers should call [BaseRequest.finalize] to get the body of the
/// request as a [ByteStream]. They shouldn't make any assumptions about the
/// state of the stream; it could have data written to it asynchronously at a
/// later point, or it could already be closed when it's returned. Any
/// internal HTTP errors should be wrapped as [ClientException]s.
@override
Future
<
StreamedResponse
>
send
(
BaseRequest
request
);
/// Sends a non-streaming [Request] and returns a non-streaming [Response].
Future
<
Response
>
_sendUnstreamed
(
String
method
,
dynamic
url
,
Map
<
String
,
String
>
headers
,
[
dynamic
body
,
Encoding
encoding
])
async
{
if
(
url
is
String
)
url
=
Uri
.
parse
(
url
);
Request
request
=
new
Request
(
method
,
url
);
if
(
headers
!=
null
)
request
.
headers
.
addAll
(
headers
);
if
(
encoding
!=
null
)
request
.
encoding
=
encoding
;
if
(
body
!=
null
)
{
if
(
body
is
String
)
{
request
.
body
=
body
;
}
else
if
(
body
is
List
)
{
request
.
bodyBytes
=
DelegatingList
.
typed
(
body
);
}
else
if
(
body
is
Map
)
{
request
.
bodyFields
=
DelegatingMap
.
typed
(
body
);
}
else
{
throw
new
ArgumentError
(
'Invalid request body "
$body
".'
);
}
}
return
Response
.
fromStream
(
await
send
(
request
));
}
/// Throws an error if [response] is not successful.
void
_checkResponseSuccess
(
dynamic
url
,
Response
response
)
{
if
(
response
.
statusCode
<
400
)
return
;
String
message
=
"Request to
$url
failed with status
${response.statusCode}
"
;
if
(
response
.
reasonPhrase
!=
null
)
{
message
=
"
$message
:
${response.reasonPhrase}
"
;
}
if
(
url
is
String
)
url
=
Uri
.
parse
(
url
);
throw
new
ClientException
(
"
$message
."
,
url
);
}
/// Closes the client and cleans up any resources associated with it. It's
/// important to close each client when it's done being used; failing to do so
/// can cause the Dart process to hang.
@override
void
close
()
{}
}
packages/flutter/lib/src/http/base_request.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'dart:collection'
;
import
'byte_stream.dart'
;
import
'client.dart'
;
import
'streamed_response.dart'
;
import
'utils.dart'
;
/// The base class for HTTP requests.
///
/// Subclasses of [BaseRequest] can be constructed manually and passed to
/// [BaseClient.send], which allows the user to provide fine-grained control
/// over the request properties. However, usually it's easier to use convenience
/// methods like [get] or [BaseClient.get].
abstract
class
BaseRequest
{
/// The HTTP method of the request. Most commonly "GET" or "POST", less
/// commonly "HEAD", "PUT", or "DELETE". Non-standard method names are also
/// supported.
final
String
method
;
/// The URL to which the request will be sent.
final
Uri
url
;
/// Creates a new HTTP request.
BaseRequest
(
this
.
method
,
this
.
url
)
:
headers
=
new
LinkedHashMap
<
String
,
String
>(
equals:
(
String
key1
,
String
key2
)
=>
key1
.
toLowerCase
()
==
key2
.
toLowerCase
(),
hashCode:
(
String
key
)
=>
key
.
toLowerCase
().
hashCode
);
/// The size of the request body, in bytes.
///
/// This defaults to `null`, which indicates that the size of the request is
/// not known in advance.
int
get
contentLength
=>
_contentLength
;
int
_contentLength
;
set
contentLength
(
int
value
)
{
if
(
value
!=
null
&&
value
<
0
)
{
throw
new
ArgumentError
(
"Invalid content length
$value
."
);
}
_checkFinalized
();
_contentLength
=
value
;
}
/// Whether a persistent connection should be maintained with the server.
/// Defaults to true.
bool
get
persistentConnection
=>
_persistentConnection
;
bool
_persistentConnection
=
true
;
set
persistentConnection
(
bool
value
)
{
_checkFinalized
();
_persistentConnection
=
value
;
}
/// Whether the client should follow redirects while resolving this request.
/// Defaults to true.
bool
get
followRedirects
=>
_followRedirects
;
bool
_followRedirects
=
true
;
set
followRedirects
(
bool
value
)
{
_checkFinalized
();
_followRedirects
=
value
;
}
/// The maximum number of redirects to follow when [followRedirects] is true.
/// If this number is exceeded the [BaseResponse] future will signal a
/// [RedirectException]. Defaults to 5.
int
get
maxRedirects
=>
_maxRedirects
;
int
_maxRedirects
=
5
;
set
maxRedirects
(
int
value
)
{
_checkFinalized
();
_maxRedirects
=
value
;
}
// TODO(nweiz): automatically parse cookies from headers
// TODO(nweiz): make this a HttpHeaders object
/// The headers for this request.
final
Map
<
String
,
String
>
headers
;
/// Whether the request has been finalized.
bool
get
finalized
=>
_finalized
;
bool
_finalized
=
false
;
/// Finalizes the HTTP request in preparation for it being sent. This freezes
/// all mutable fields and returns a single-subscription [ByteStream] that
/// emits the body of the request.
///
/// The base implementation of this returns null rather than a [ByteStream];
/// subclasses are responsible for creating the return value, which should be
/// single-subscription to ensure that no data is dropped. They should also
/// freeze any additional mutable fields they add that don't make sense to
/// change after the request headers are sent.
ByteStream
finalize
()
{
// TODO(nweiz): freeze headers
if
(
finalized
)
throw
new
StateError
(
"Can't finalize a finalized Request."
);
_finalized
=
true
;
return
null
;
}
/// Sends this request.
///
/// This automatically initializes a new [Client] and closes that client once
/// the request is complete. If you're planning on making multiple requests to
/// the same server, you should use a single [Client] for all of those
/// requests.
Future
<
StreamedResponse
>
send
()
async
{
Client
client
=
new
Client
();
try
{
StreamedResponse
response
=
await
client
.
send
(
this
);
Stream
<
dynamic
>
stream
=
onDone
(
response
.
stream
,
client
.
close
);
return
new
StreamedResponse
(
new
ByteStream
(
stream
),
response
.
statusCode
,
contentLength:
response
.
contentLength
,
request:
response
.
request
,
headers:
response
.
headers
,
isRedirect:
response
.
isRedirect
,
persistentConnection:
response
.
persistentConnection
,
reasonPhrase:
response
.
reasonPhrase
);
}
catch
(
ex
)
{
client
.
close
();
rethrow
;
}
}
// Throws an error if this request has been finalized.
void
_checkFinalized
()
{
if
(!
finalized
)
return
;
throw
new
StateError
(
"Can't modify a finalized Request."
);
}
@override
String
toString
()
=>
"
$method
$url
"
;
}
packages/flutter/lib/src/http/base_response.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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
'base_request.dart'
;
/// The base class for HTTP responses.
///
/// Subclasses of [BaseResponse] are usually not constructed manually; instead,
/// they're returned by [BaseClient.send] or other HTTP client methods.
abstract
class
BaseResponse
{
/// The (frozen) request that triggered this response.
final
BaseRequest
request
;
/// The status code of the response.
final
int
statusCode
;
/// The reason phrase associated with the status code.
final
String
reasonPhrase
;
/// The size of the response body, in bytes.
///
/// If the size of the request is not known in advance, this is `null`.
final
int
contentLength
;
// TODO(nweiz): automatically parse cookies from headers
// TODO(nweiz): make this a HttpHeaders object.
/// The headers for this response.
final
Map
<
String
,
String
>
headers
;
/// Whether this response is a redirect.
final
bool
isRedirect
;
/// Whether the server requested that a persistent connection be maintained.
final
bool
persistentConnection
;
/// Creates a new HTTP response.
BaseResponse
(
this
.
statusCode
,
{
this
.
contentLength
,
this
.
request
,
this
.
headers
:
const
<
String
,
String
>{},
this
.
isRedirect
:
false
,
this
.
persistentConnection
:
true
,
this
.
reasonPhrase
})
{
if
(
statusCode
<
100
)
{
throw
new
ArgumentError
(
"Invalid status code
$statusCode
."
);
}
else
if
(
contentLength
!=
null
&&
contentLength
<
0
)
{
throw
new
ArgumentError
(
"Invalid content length
$contentLength
."
);
}
}
}
packages/flutter/lib/src/http/byte_stream.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2013, 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:async'
;
import
'dart:convert'
;
import
'dart:typed_data'
;
/// A stream of chunks of bytes representing a single piece of data.
class
ByteStream
extends
StreamView
<
List
<
int
>>
{
ByteStream
(
Stream
<
List
<
int
>>
stream
)
:
super
(
stream
);
/// Returns a single-subscription byte stream that will emit the given bytes
/// in a single chunk.
factory
ByteStream
.
fromBytes
(
List
<
int
>
bytes
)
=>
new
ByteStream
(
new
Stream
<
dynamic
>.
fromIterable
(<
List
<
int
>>[
bytes
]));
/// Collects the data of this stream in a [Uint8List].
Future
<
Uint8List
>
toBytes
()
{
Completer
<
Uint8List
>
completer
=
new
Completer
<
Uint8List
>();
dynamic
sink
=
new
ByteConversionSink
.
withCallback
((
dynamic
bytes
)
=>
completer
.
complete
(
new
Uint8List
.
fromList
(
bytes
)));
listen
(
sink
.
add
,
onError:
completer
.
completeError
,
onDone:
sink
.
close
,
cancelOnError:
true
);
return
completer
.
future
;
}
/// Collect the data of this stream in a [String], decoded according to
/// [encoding], which defaults to `UTF8`.
Future
<
String
>
bytesToString
([
Encoding
encoding
=
UTF8
])
=>
encoding
.
decodeStream
(
this
);
Stream
<
String
>
toStringStream
([
Encoding
encoding
=
UTF8
])
=>
encoding
.
decoder
.
bind
(
this
);
}
packages/flutter/lib/src/http/client.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'dart:convert'
;
import
'dart:typed_data'
;
import
'base_client.dart'
;
import
'base_request.dart'
;
import
'io_client.dart'
;
import
'response.dart'
;
import
'streamed_response.dart'
;
typedef
Client
ClientOverride
(
);
/// The interface for HTTP clients that take care of maintaining persistent
/// connections across multiple requests to the same server. If you only need to
/// send a single request, it's usually easier to use [head], [get], [post],
/// [put], [patch], or [delete] instead.
///
/// When creating an HTTP client class with additional functionality, you must
/// extend [BaseClient] rather than [Client]. In most cases, you can wrap
/// another instance of [Client] and add functionality on top of that. This
/// allows all classes implementing [Client] to be mutually composable.
abstract
class
Client
{
/// Creates a new client.
///
/// Currently this will create an [IOClient] if `dart:io` is available and
/// throw an [UnsupportedError] otherwise. In the future, it will create a
/// [BrowserClient] if `dart:html` is available.
factory
Client
()
{
return
clientOverride
==
null
?
new
IOClient
()
:
clientOverride
();
}
static
ClientOverride
clientOverride
;
/// Sends an HTTP HEAD request with the given headers to the given URL, which
/// can be a [Uri] or a [String].
///
/// For more fine-grained control over the request, use [send] instead.
Future
<
Response
>
head
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
});
/// Sends an HTTP GET request with the given headers to the given URL, which
/// can be a [Uri] or a [String].
///
/// For more fine-grained control over the request, use [send] instead.
Future
<
Response
>
get
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
});
/// Sends an HTTP POST request with the given headers and body to the given
/// URL, which can be a [Uri] or a [String].
///
/// [body] sets the body of the request. It can be a [String], a [List<int>]
/// or a [Map<String, String>]. If it's a String, it's encoded using
/// [encoding] and used as the body of the request. The content-type of the
/// request will default to "text/plain".
///
/// If [body] is a List, it's used as a list of bytes for the body of the
/// request.
///
/// If [body] is a Map, it's encoded as form fields using [encoding]. The
/// content-type of the request will be set to
/// `"application/x-www-form-urlencoded"`; this cannot be overridden.
///
/// [encoding] defaults to [UTF8].
///
/// For more fine-grained control over the request, use [send] instead.
Future
<
Response
>
post
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
,
dynamic
body
,
Encoding
encoding
});
/// Sends an HTTP PUT request with the given headers and body to the given
/// URL, which can be a [Uri] or a [String].
///
/// [body] sets the body of the request. It can be a [String], a [List<int>]
/// or a [Map<String, String>]. If it's a String, it's encoded using
/// [encoding] and used as the body of the request. The content-type of the
/// request will default to "text/plain".
///
/// If [body] is a List, it's used as a list of bytes for the body of the
/// request.
///
/// If [body] is a Map, it's encoded as form fields using [encoding]. The
/// content-type of the request will be set to
/// `"application/x-www-form-urlencoded"`; this cannot be overridden.
///
/// [encoding] defaults to [UTF8].
///
/// For more fine-grained control over the request, use [send] instead.
Future
<
Response
>
put
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
,
dynamic
body
,
Encoding
encoding
});
/// Sends an HTTP PATCH request with the given headers and body to the given
/// URL, which can be a [Uri] or a [String].
///
/// [body] sets the body of the request. It can be a [String], a [List<int>]
/// or a [Map<String, String>]. If it's a String, it's encoded using
/// [encoding] and used as the body of the request. The content-type of the
/// request will default to "text/plain".
///
/// If [body] is a List, it's used as a list of bytes for the body of the
/// request.
///
/// If [body] is a Map, it's encoded as form fields using [encoding]. The
/// content-type of the request will be set to
/// `"application/x-www-form-urlencoded"`; this cannot be overridden.
///
/// [encoding] defaults to [UTF8].
///
/// For more fine-grained control over the request, use [send] instead.
Future
<
Response
>
patch
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
,
dynamic
body
,
Encoding
encoding
});
/// Sends an HTTP DELETE request with the given headers to the given URL,
/// which can be a [Uri] or a [String].
///
/// For more fine-grained control over the request, use [send] instead.
Future
<
Response
>
delete
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
});
/// Sends an HTTP GET request with the given headers to the given URL, which
/// can be a [Uri] or a [String], and returns a Future that completes to the
/// body of the response as a String.
///
/// The Future will emit a [ClientException] if the response doesn't have a
/// success status code.
///
/// For more fine-grained control over the request and response, use [send] or
/// [get] instead.
Future
<
String
>
read
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
});
/// Sends an HTTP GET request with the given headers to the given URL, which
/// can be a [Uri] or a [String], and returns a Future that completes to the
/// body of the response as a list of bytes.
///
/// The Future will emit a [ClientException] if the response doesn't have a
/// success status code.
///
/// For more fine-grained control over the request and response, use [send] or
/// [get] instead.
Future
<
Uint8List
>
readBytes
(
dynamic
url
,
{
Map
<
String
,
String
>
headers
});
/// Sends an HTTP request and asynchronously returns the response.
Future
<
StreamedResponse
>
send
(
BaseRequest
request
);
/// Closes the client and cleans up any resources associated with it. It's
/// important to close each client when it's done being used; failing to do so
/// can cause the Dart process to hang.
void
close
();
}
packages/flutter/lib/src/http/exception.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2014, 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.
/// An exception caused by an error in a pkg/http client.
class
ClientException
implements
Exception
{
final
String
message
;
/// The URL of the HTTP request or response that failed.
final
Uri
uri
;
ClientException
(
this
.
message
,
[
this
.
uri
]);
@override
String
toString
()
=>
message
;
}
packages/flutter/lib/src/http/http.dart
View file @
400585cb
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/http/io.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2014, 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:io'
as
io
;
/// Whether `dart:io` is supported on this platform.
bool
get
supported
=>
true
;
/// Asserts that the [name]d `dart:io` feature is supported on this platform.
///
/// If `dart:io` doesn't work on this platform, this throws an
/// [UnsupportedError].
void
assertSupported
(
String
name
)
{}
/// Creates a new `dart:io` HttpClient instance.
io
.
HttpClient
newHttpClient
(
)
=>
new
io
.
HttpClient
();
/// Creates a new `dart:io` File instance with the given [path].
io
.
File
newFile
(
String
path
)
=>
new
io
.
File
(
path
);
/// Returns whether [error] is a `dart:io` HttpException.
bool
isHttpException
(
dynamic
error
)
=>
error
is
io
.
HttpException
;
/// Returns whether [client] is a `dart:io` HttpClient.
bool
isHttpClient
(
dynamic
client
)
=>
client
is
io
.
HttpClient
;
packages/flutter/lib/src/http/io_client.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'package:async/async.dart'
;
import
'base_client.dart'
;
import
'base_request.dart'
;
import
'exception.dart'
;
import
'io.dart'
as
io
;
import
'streamed_response.dart'
;
/// A `dart:io`-based HTTP client.
///
/// This is the default client when running on the command line.
class
IOClient
extends
BaseClient
{
/// The underlying `dart:io` HTTP client.
dynamic
_inner
;
/// Creates a new HTTP client.
///
/// [innerClient] must be a `dart:io` HTTP client. If it's not passed, a
/// default one will be instantiated.
IOClient
([
dynamic
innerClient
])
{
io
.
assertSupported
(
"IOClient"
);
if
(
innerClient
!=
null
)
{
// TODO(nweiz): remove this assert when we can type [innerClient]
// properly.
assert
(
io
.
isHttpClient
(
innerClient
));
_inner
=
innerClient
;
}
else
{
_inner
=
io
.
newHttpClient
();
}
}
/// Sends an HTTP request and asynchronously returns the response.
@override
Future
<
StreamedResponse
>
send
(
BaseRequest
request
)
async
{
dynamic
stream
=
request
.
finalize
();
try
{
dynamic
ioRequest
=
await
_inner
.
openUrl
(
request
.
method
,
request
.
url
);
ioRequest
..
followRedirects
=
request
.
followRedirects
..
maxRedirects
=
request
.
maxRedirects
..
contentLength
=
request
.
contentLength
==
null
?
-
1
:
request
.
contentLength
..
persistentConnection
=
request
.
persistentConnection
;
request
.
headers
.
forEach
((
String
name
,
String
value
)
{
ioRequest
.
headers
.
set
(
name
,
value
);
});
dynamic
response
=
await
stream
.
pipe
(
DelegatingStreamConsumer
.
typed
(
ioRequest
));
Map
<
String
,
dynamic
>
headers
=
<
String
,
dynamic
>{};
response
.
headers
.
forEach
((
String
key
,
dynamic
values
)
{
headers
[
key
]
=
values
.
join
(
','
);
});
return
new
StreamedResponse
(
DelegatingStream
.
typed
/*<List<int>>*/
(
response
).
handleError
((
dynamic
error
)
=>
throw
new
ClientException
(
error
.
message
,
error
.
uri
),
test:
(
dynamic
error
)
=>
io
.
isHttpException
(
error
)),
response
.
statusCode
,
contentLength:
response
.
contentLength
==
-
1
?
null
:
response
.
contentLength
,
request:
request
,
headers:
headers
,
isRedirect:
response
.
isRedirect
,
persistentConnection:
response
.
persistentConnection
,
reasonPhrase:
response
.
reasonPhrase
);
}
catch
(
error
)
{
if
(!
io
.
isHttpException
(
error
))
rethrow
;
throw
new
ClientException
(
error
.
message
,
error
.
uri
);
}
}
/// Closes the client. This terminates all active connections. If a client
/// remains unclosed, the Dart process may not terminate.
@override
void
close
()
{
if
(
_inner
!=
null
)
_inner
.
close
(
force:
true
);
_inner
=
null
;
}
}
packages/flutter/lib/src/http/mock_client.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'dart:typed_data'
;
import
'base_client.dart'
;
import
'base_request.dart'
;
import
'byte_stream.dart'
;
import
'request.dart'
;
import
'response.dart'
;
import
'streamed_response.dart'
;
// TODO(nweiz): once Dart has some sort of Rack- or WSGI-like standard for
// server APIs, MockClient should conform to it.
/// A mock HTTP client designed for use when testing code that uses
/// [BaseClient]. This client allows you to define a handler callback for all
/// requests that are made through it so that you can mock a server without
/// having to send real HTTP requests.
class
MockClient
extends
BaseClient
{
/// The handler for receiving [StreamedRequest]s and sending
/// [StreamedResponse]s.
final
MockClientStreamHandler
_handler
;
/// Creates a [MockClient] with a handler that receives [Request]s and sends
/// [Response]s.
MockClient
(
MockClientHandler
fn
)
:
this
.
_
((
Request
baseRequest
,
ByteStream
bodyStream
)
{
return
bodyStream
.
toBytes
().
then
((
Uint8List
bodyBytes
)
{
Request
request
=
new
Request
(
baseRequest
.
method
,
baseRequest
.
url
)
..
persistentConnection
=
baseRequest
.
persistentConnection
..
followRedirects
=
baseRequest
.
followRedirects
..
maxRedirects
=
baseRequest
.
maxRedirects
..
headers
.
addAll
(
baseRequest
.
headers
)
..
bodyBytes
=
bodyBytes
..
finalize
();
return
fn
(
request
);
}).
then
((
Response
response
)
{
return
new
StreamedResponse
(
new
ByteStream
.
fromBytes
(
response
.
bodyBytes
),
response
.
statusCode
,
contentLength:
response
.
contentLength
,
request:
baseRequest
,
headers:
response
.
headers
,
isRedirect:
response
.
isRedirect
,
persistentConnection:
response
.
persistentConnection
,
reasonPhrase:
response
.
reasonPhrase
);
});
});
MockClient
.
_
(
this
.
_handler
);
/// Creates a [MockClient] with a handler that receives [StreamedRequest]s and
/// sends [StreamedResponse]s.
MockClient
.
streaming
(
MockClientStreamHandler
fn
)
:
this
.
_
((
Request
request
,
ByteStream
bodyStream
)
{
return
fn
(
request
,
bodyStream
).
then
((
StreamedResponse
response
)
{
return
new
StreamedResponse
(
response
.
stream
,
response
.
statusCode
,
contentLength:
response
.
contentLength
,
request:
request
,
headers:
response
.
headers
,
isRedirect:
response
.
isRedirect
,
persistentConnection:
response
.
persistentConnection
,
reasonPhrase:
response
.
reasonPhrase
);
});
});
/// Sends a request.
@override
Future
<
StreamedResponse
>
send
(
BaseRequest
request
)
async
{
ByteStream
bodyStream
=
request
.
finalize
();
return
await
_handler
(
request
,
bodyStream
);
}
}
/// A handler function that receives [StreamedRequest]s and sends
/// [StreamedResponse]s. Note that [request] will be finalized.
typedef
Future
<
StreamedResponse
>
MockClientStreamHandler
(
BaseRequest
request
,
ByteStream
bodyStream
);
/// A handler function that receives [Request]s and sends [Response]s. Note that
/// [request] will be finalized.
typedef
Future
<
Response
>
MockClientHandler
(
Request
request
);
packages/flutter/lib/src/http/mojo_client.dart
deleted
100644 → 0
View file @
4f0eff31
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/http/multipart_file.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'dart:convert'
;
import
'package:async/async.dart'
;
import
'package:http_parser/http_parser.dart'
;
import
'package:path/path.dart'
as
path
;
import
'byte_stream.dart'
;
import
'io.dart'
as
io
;
import
'utils.dart'
;
/// A file to be uploaded as part of a [MultipartRequest]. This doesn't need to
/// correspond to a physical file.
class
MultipartFile
{
/// The name of the form field for the file.
final
String
field
;
/// The size of the file in bytes. This must be known in advance, even if this
/// file is created from a [ByteStream].
final
int
length
;
/// The basename of the file. May be null.
final
String
filename
;
/// The content-type of the file. Defaults to `application/octet-stream`.
final
MediaType
contentType
;
/// The stream that will emit the file's contents.
final
ByteStream
_stream
;
/// Creates a new [MultipartFile] from a chunked [Stream] of bytes. The length
/// of the file in bytes must be known in advance. If it's not, read the data
/// from the stream and use [MultipartFile.fromBytes] instead.
///
/// [contentType] currently defaults to `application/octet-stream`, but in the
/// future may be inferred from [filename].
MultipartFile
(
this
.
field
,
Stream
<
List
<
int
>>
stream
,
this
.
length
,
{
this
.
filename
,
MediaType
contentType
})
:
this
.
_stream
=
toByteStream
(
stream
),
this
.
contentType
=
contentType
!=
null
?
contentType
:
new
MediaType
(
"application"
,
"octet-stream"
);
/// Creates a new [MultipartFile] from a byte array.
///
/// [contentType] currently defaults to `application/octet-stream`, but in the
/// future may be inferred from [filename].
factory
MultipartFile
.
fromBytes
(
String
field
,
List
<
int
>
value
,
{
String
filename
,
MediaType
contentType
})
{
ByteStream
stream
=
new
ByteStream
.
fromBytes
(
value
);
return
new
MultipartFile
(
field
,
stream
,
value
.
length
,
filename:
filename
,
contentType:
contentType
);
}
/// Creates a new [MultipartFile] from a string.
///
/// The encoding to use when translating [value] into bytes is taken from
/// [contentType] if it has a charset set. Otherwise, it defaults to UTF-8.
/// [contentType] currently defaults to `text/plain; charset=utf-8`, but in
/// the future may be inferred from [filename].
factory
MultipartFile
.
fromString
(
String
field
,
String
value
,
{
String
filename
,
MediaType
contentType
})
{
contentType
=
contentType
==
null
?
new
MediaType
(
"text"
,
"plain"
)
:
contentType
;
Encoding
encoding
=
encodingForCharset
(
contentType
.
parameters
[
'charset'
],
UTF8
);
contentType
=
contentType
.
change
(
parameters:
<
String
,
String
>{
'charset'
:
encoding
.
name
});
return
new
MultipartFile
.
fromBytes
(
field
,
encoding
.
encode
(
value
),
filename:
filename
,
contentType:
contentType
);
}
/// Whether [finalize] has been called.
bool
get
isFinalized
=>
_isFinalized
;
bool
_isFinalized
=
false
;
// TODO(nweiz): Infer the content-type from the filename.
/// Creates a new [MultipartFile] from a path to a file on disk.
///
/// [filename] defaults to the basename of [filePath]. [contentType] currently
/// defaults to `application/octet-stream`, but in the future may be inferred
/// from [filename].
///
/// This can only be used in an environment that supports "dart:io".
static
Future
<
MultipartFile
>
fromPath
(
String
field
,
String
filePath
,
{
String
filename
,
MediaType
contentType
})
async
{
io
.
assertSupported
(
"MultipartFile.fromPath"
);
if
(
filename
==
null
)
filename
=
path
.
basename
(
filePath
);
dynamic
file
=
io
.
newFile
(
filePath
);
int
length
=
await
file
.
length
();
ByteStream
stream
=
new
ByteStream
(
DelegatingStream
.
typed
(
file
.
openRead
()));
return
new
MultipartFile
(
field
,
stream
,
length
,
filename:
filename
,
contentType:
contentType
);
}
// Finalizes the file in preparation for it being sent as part of a
// [MultipartRequest]. This returns a [ByteStream] that should emit the body
// of the file. The stream may be closed to indicate an empty file.
ByteStream
finalize
()
{
if
(
isFinalized
)
{
throw
new
StateError
(
"Can't finalize a finalized MultipartFile."
);
}
_isFinalized
=
true
;
return
_stream
;
}
}
packages/flutter/lib/src/http/multipart_request.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2013, 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:async'
;
import
'dart:convert'
;
import
'dart:math'
;
import
'base_request.dart'
;
import
'byte_stream.dart'
;
import
'multipart_file.dart'
;
import
'utils.dart'
;
final
RegExp
_newlineRegExp
=
new
RegExp
(
r"\r\n|\r|\n"
);
/// A `multipart/form-data` request. Such a request has both string [fields],
/// which function as normal form fields, and (potentially streamed) binary
/// [files].
///
/// This request automatically sets the Content-Type header to
/// `multipart/form-data`. This value will override any value set by the user.
///
/// var uri = Uri.parse("http://pub.dartlang.org/packages/create");
/// var request = new http.MultipartRequest("POST", url);
/// request.fields['user'] = 'nweiz@google.com';
/// request.files.add(new http.MultipartFile.fromFile(
/// 'package',
/// new File('build/package.tar.gz'),
/// contentType: new MediaType('application', 'x-tar'));
/// request.send().then((response) {
/// if (response.statusCode == 200) print("Uploaded!");
/// });
class
MultipartRequest
extends
BaseRequest
{
/// The total length of the multipart boundaries used when building the
/// request body. According to http://tools.ietf.org/html/rfc1341.html, this
/// can't be longer than 70.
static
const
int
_BOUNDARY_LENGTH
=
70
;
static
final
Random
_random
=
new
Random
();
/// The form fields to send for this request.
final
Map
<
String
,
String
>
fields
;
/// The private version of [files].
final
List
<
MultipartFile
>
_files
;
/// Creates a new [MultipartRequest].
MultipartRequest
(
String
method
,
Uri
url
)
:
fields
=
<
String
,
String
>{},
_files
=
<
MultipartFile
>[],
super
(
method
,
url
);
/// The list of files to upload for this request.
List
<
MultipartFile
>
get
files
=>
_files
;
/// The total length of the request body, in bytes. This is calculated from
/// [fields] and [files] and cannot be set manually.
@override
int
get
contentLength
{
int
length
=
0
;
fields
.
forEach
((
String
name
,
String
value
)
{
length
+=
"--"
.
length
+
_BOUNDARY_LENGTH
+
"
\r\n
"
.
length
+
UTF8
.
encode
(
_headerForField
(
name
,
value
)).
length
+
UTF8
.
encode
(
value
).
length
+
"
\r\n
"
.
length
;
});
for
(
MultipartFile
file
in
_files
)
{
length
+=
"--"
.
length
+
_BOUNDARY_LENGTH
+
"
\r\n
"
.
length
+
UTF8
.
encode
(
_headerForFile
(
file
)).
length
+
file
.
length
+
"
\r\n
"
.
length
;
}
return
length
+
"--"
.
length
+
_BOUNDARY_LENGTH
+
"--
\r\n
"
.
length
;
}
@override
set
contentLength
(
int
value
)
{
throw
new
UnsupportedError
(
"Cannot set the contentLength property of "
"multipart requests."
);
}
/// Freezes all mutable fields and returns a single-subscription [ByteStream]
/// that will emit the request body.
@override
ByteStream
finalize
()
{
// TODO(nweiz): freeze fields and files
String
boundary
=
_boundaryString
();
headers
[
'content-type'
]
=
'multipart/form-data; boundary="
$boundary
"'
;
super
.
finalize
();
StreamController
<
List
<
int
>>
controller
=
new
StreamController
<
List
<
int
>>(
sync:
true
);
void
writeAscii
(
String
string
)
{
controller
.
add
(
UTF8
.
encode
(
string
));
}
dynamic
writeUtf8
(
String
string
)
=>
controller
.
add
(
UTF8
.
encode
(
string
));
dynamic
writeLine
()
=>
controller
.
add
(<
int
>[
13
,
10
]);
// \r\n
fields
.
forEach
((
String
name
,
String
value
)
{
writeAscii
(
'--
$boundary
\r\n
'
);
writeAscii
(
_headerForField
(
name
,
value
));
writeUtf8
(
value
);
writeLine
();
});
Future
.
forEach
(
_files
,
(
MultipartFile
file
)
{
writeAscii
(
'--
$boundary
\r\n
'
);
writeAscii
(
_headerForFile
(
file
));
return
writeStreamToSink
(
file
.
finalize
(),
controller
)
.
then
((
_
)
=>
writeLine
());
}).
then
((
_
)
{
// TODO(nweiz): pass any errors propagated through this future on to
// the stream. See issue 3657.
writeAscii
(
'--
$boundary
--
\r\n
'
);
controller
.
close
();
});
return
new
ByteStream
(
controller
.
stream
);
}
/// All character codes that are valid in multipart boundaries. From
/// http://tools.ietf.org/html/rfc2046#section-5.1.1.
static
const
List
<
int
>
_BOUNDARY_CHARACTERS
=
const
<
int
>[
39
,
40
,
41
,
43
,
95
,
44
,
45
,
46
,
47
,
58
,
61
,
63
,
48
,
49
,
50
,
51
,
52
,
53
,
54
,
55
,
56
,
57
,
65
,
66
,
67
,
68
,
69
,
70
,
71
,
72
,
73
,
74
,
75
,
76
,
77
,
78
,
79
,
80
,
81
,
82
,
83
,
84
,
85
,
86
,
87
,
88
,
89
,
90
,
97
,
98
,
99
,
100
,
101
,
102
,
103
,
104
,
105
,
106
,
107
,
108
,
109
,
110
,
111
,
112
,
113
,
114
,
115
,
116
,
117
,
118
,
119
,
120
,
121
,
122
];
/// Returns the header string for a field. The return value is guaranteed to
/// contain only ASCII characters.
String
_headerForField
(
String
name
,
String
value
)
{
String
header
=
'content-disposition: form-data; name="
${_browserEncode(name)}
"'
;
if
(!
isPlainAscii
(
value
))
{
header
=
'
$header
\r\n
'
'content-type: text/plain; charset=utf-8
\r\n
'
'content-transfer-encoding: binary'
;
}
return
'
$header
\r\n\r\n
'
;
}
/// Returns the header string for a file. The return value is guaranteed to
/// contain only ASCII characters.
String
_headerForFile
(
MultipartFile
file
)
{
String
header
=
'content-type:
${file.contentType}
\r\n
'
'content-disposition: form-data; name="
${_browserEncode(file.field)}
"'
;
if
(
file
.
filename
!=
null
)
{
header
=
'
$header
; filename="
${_browserEncode(file.filename)}
"'
;
}
return
'
$header
\r\n\r\n
'
;
}
/// Encode [value] in the same way browsers do.
String
_browserEncode
(
String
value
)
{
// http://tools.ietf.org/html/rfc2388 mandates some complex encodings for
// field names and file names, but in practice user agents seem not to
// follow this at all. Instead, they URL-encode `\r`, `\n`, and `\r\n` as
// `\r\n`; URL-encode `"`; and do nothing else (even for `%` or non-ASCII
// characters). We follow their behavior.
return
value
.
replaceAll
(
_newlineRegExp
,
"%0D%0A"
).
replaceAll
(
'"'
,
"%22"
);
}
/// Returns a randomly-generated multipart boundary string
String
_boundaryString
()
{
String
prefix
=
"dart-http-boundary-"
;
List
<
int
>
list
=
new
List
<
int
>.
generate
(
_BOUNDARY_LENGTH
-
prefix
.
length
,
(
int
index
)
=>
_BOUNDARY_CHARACTERS
[
_random
.
nextInt
(
_BOUNDARY_CHARACTERS
.
length
)],
growable:
false
);
return
"
$prefix${new String.fromCharCodes(list)}
"
;
}
}
packages/flutter/lib/src/http/request.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2013, 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:typed_data'
;
import
'package:http_parser/http_parser.dart'
;
import
'base_request.dart'
;
import
'byte_stream.dart'
;
import
'utils.dart'
;
/// An HTTP request where the entire request body is known in advance.
class
Request
extends
BaseRequest
{
/// Creates a new HTTP request.
Request
(
String
method
,
Uri
url
)
:
_defaultEncoding
=
UTF8
,
_bodyBytes
=
new
Uint8List
(
0
),
super
(
method
,
url
);
/// The size of the request body, in bytes. This is calculated from
/// [bodyBytes].
///
/// The content length cannot be set for [Request], since it's automatically
/// calculated from [bodyBytes].
@override
int
get
contentLength
=>
bodyBytes
.
length
;
@override
set
contentLength
(
int
value
)
{
throw
new
UnsupportedError
(
"Cannot set the contentLength property of "
"non-streaming Request objects."
);
}
/// The default encoding to use when converting between [bodyBytes] and
/// [body]. This is only used if [encoding] hasn't been manually set and if
/// the content-type header has no encoding information.
Encoding
_defaultEncoding
;
/// The encoding used for the request. This encoding is used when converting
/// between [bodyBytes] and [body].
///
/// If the request has a `Content-Type` header and that header has a `charset`
/// parameter, that parameter's value is used as the encoding. Otherwise, if
/// [encoding] has been set manually, that encoding is used. If that hasn't
/// been set either, this defaults to [UTF8].
///
/// If the `charset` parameter's value is not a known [Encoding], reading this
/// will throw a [FormatException].
///
/// If the request has a `Content-Type` header, setting this will set the
/// charset parameter on that header.
Encoding
get
encoding
{
if
(
_contentType
==
null
||
!
_contentType
.
parameters
.
containsKey
(
'charset'
))
{
return
_defaultEncoding
;
}
return
requiredEncodingForCharset
(
_contentType
.
parameters
[
'charset'
]);
}
set
encoding
(
Encoding
value
)
{
_checkFinalized
();
_defaultEncoding
=
value
;
MediaType
contentType
=
_contentType
;
if
(
contentType
==
null
)
return
;
_contentType
=
contentType
.
change
(
parameters:
<
String
,
String
>{
'charset'
:
value
.
name
});
}
// TODO(nweiz): make this return a read-only view
/// The bytes comprising the body of the request. This is converted to and
/// from [body] using [encoding].
///
/// This list should only be set, not be modified in place.
Uint8List
get
bodyBytes
=>
_bodyBytes
;
Uint8List
_bodyBytes
;
set
bodyBytes
(
List
<
int
>
value
)
{
_checkFinalized
();
_bodyBytes
=
toUint8List
(
value
);
}
/// The body of the request as a string. This is converted to and from
/// [bodyBytes] using [encoding].
///
/// When this is set, if the request does not yet have a `Content-Type`
/// header, one will be added with the type `text/plain`. Then the `charset`
/// parameter of the `Content-Type` header (whether new or pre-existing) will
/// be set to [encoding] if it wasn't already set.
String
get
body
=>
encoding
.
decode
(
bodyBytes
);
set
body
(
String
value
)
{
bodyBytes
=
encoding
.
encode
(
value
);
MediaType
contentType
=
_contentType
;
if
(
contentType
==
null
)
{
_contentType
=
new
MediaType
(
"text"
,
"plain"
,
<
String
,
String
>{
'charset'
:
encoding
.
name
});
}
else
if
(!
contentType
.
parameters
.
containsKey
(
'charset'
))
{
_contentType
=
contentType
.
change
(
parameters:
<
String
,
String
>{
'charset'
:
encoding
.
name
});
}
}
/// The form-encoded fields in the body of the request as a map from field
/// names to values. The form-encoded body is converted to and from
/// [bodyBytes] using [encoding] (in the same way as [body]).
///
/// If the request doesn't have a `Content-Type` header of
/// `application/x-www-form-urlencoded`, reading this will throw a
/// [StateError].
///
/// If the request has a `Content-Type` header with a type other than
/// `application/x-www-form-urlencoded`, setting this will throw a
/// [StateError]. Otherwise, the content type will be set to
/// `application/x-www-form-urlencoded`.
///
/// This map should only be set, not modified in place.
Map
<
String
,
String
>
get
bodyFields
{
MediaType
contentType
=
_contentType
;
if
(
contentType
==
null
||
contentType
.
mimeType
!=
"application/x-www-form-urlencoded"
)
{
throw
new
StateError
(
'Cannot access the body fields of a Request without '
'content-type "application/x-www-form-urlencoded".'
);
}
return
Uri
.
splitQueryString
(
body
,
encoding:
encoding
);
}
set
bodyFields
(
Map
<
String
,
String
>
fields
)
{
MediaType
contentType
=
_contentType
;
if
(
contentType
==
null
)
{
_contentType
=
new
MediaType
(
"application"
,
"x-www-form-urlencoded"
);
}
else
if
(
contentType
.
mimeType
!=
"application/x-www-form-urlencoded"
)
{
throw
new
StateError
(
'Cannot set the body fields of a Request with '
'content-type "
${contentType.mimeType}
".'
);
}
this
.
body
=
mapToQuery
(
fields
,
encoding:
encoding
);
}
/// Freezes all mutable fields and returns a single-subscription [ByteStream]
/// containing the request body.
@override
ByteStream
finalize
()
{
super
.
finalize
();
return
new
ByteStream
.
fromBytes
(
bodyBytes
);
}
/// The `Content-Type` header of the request (if it exists) as a
/// [MediaType].
MediaType
get
_contentType
{
String
contentType
=
headers
[
'content-type'
];
if
(
contentType
==
null
)
return
null
;
return
new
MediaType
.
parse
(
contentType
);
}
set
_contentType
(
MediaType
value
)
{
headers
[
'content-type'
]
=
value
.
toString
();
}
/// Throw an error if this request has been finalized.
void
_checkFinalized
()
{
if
(!
finalized
)
return
;
throw
new
StateError
(
"Can't modify a finalized Request."
);
}
}
packages/flutter/lib/src/http/response.dart
View file @
400585cb
...
...
@@ -2,103 +2,94 @@
// 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:async'
;
import
'dart:convert'
;
import
'dart:typed_data'
;
/// An HTTP response where the entire response body is known in advance.
class
Response
{
/// Creates a [Response] object with the given fields.
///
/// If [bodyBytes] is non-null, it is used to populate [body].
Response
.
bytes
(
this
.
bodyBytes
,
this
.
statusCode
,
{
this
.
headers
:
const
<
String
,
String
>{},
this
.
error
});
import
'package:http_parser/http_parser.dart'
;
/// The result of decoding [bodyBytes] using the character encoding declared
/// in the headers.
///
/// Defaults to [LATIN1] (ISO 8859-1).
///
/// If [bodyBytes] is null, this will also be null.
String
get
body
=>
bodyBytes
==
null
?
null
:
_encodingForHeaders
(
headers
).
decode
(
bodyBytes
);
import
'base_request.dart'
;
import
'base_response.dart'
;
import
'streamed_response.dart'
;
import
'utils.dart'
;
/// The raw byte stream.
/// An HTTP response where the entire response body is known in advance.
class
Response
extends
BaseResponse
{
/// The bytes comprising the body of this response.
final
Uint8List
bodyBytes
;
/// The HTTP result code.
///
/// The code 500 is used when no status code could be obtained from the host.
final
int
statusCode
;
/// Creates a new HTTP response with a string body.
Response
(
String
body
,
int
statusCode
,
{
BaseRequest
request
,
Map
<
String
,
String
>
headers:
const
<
String
,
String
>{},
bool
isRedirect:
false
,
bool
persistentConnection:
true
,
String
reasonPhrase
})
:
this
.
bytes
(
_encodingForHeaders
(
headers
).
encode
(
body
),
statusCode
,
request:
request
,
headers:
headers
,
isRedirect:
isRedirect
,
persistentConnection:
persistentConnection
,
reasonPhrase:
reasonPhrase
);
/// Error information, if any. This may be populated if the [statusCode] is
/// 4xx or 5xx. This may be a string (e.g. the status line from the server) or
/// an [Exception], but in either case the object should have a useful
/// [toString] implementation that returns a human-readable value.
final
dynamic
error
;
/// Create a new HTTP response with a byte array body.
Response
.
bytes
(
List
<
int
>
bodyBytes
,
int
statusCode
,
{
BaseRequest
request
,
Map
<
String
,
String
>
headers:
const
<
String
,
String
>{},
bool
isRedirect:
false
,
bool
persistentConnection:
true
,
String
reasonPhrase
})
:
bodyBytes
=
toUint8List
(
bodyBytes
),
super
(
statusCode
,
contentLength:
bodyBytes
.
length
,
request:
request
,
headers:
headers
,
isRedirect:
isRedirect
,
persistentConnection:
persistentConnection
,
reasonPhrase:
reasonPhrase
);
/// The headers for this response.
final
Map
<
String
,
String
>
headers
;
}
bool
_isSpace
(
String
c
)
{
return
c
==
' '
||
c
==
'
\t
'
||
c
==
'
\n
'
||
c
==
'
\r
'
||
c
==
'
\
f'
;
}
int
_skipSpaces
(
String
string
,
int
index
)
{
while
(
index
<
string
.
length
&&
_isSpace
(
string
[
index
]))
index
+=
1
;
return
index
;
}
/// The body of the response as a string. This is converted from [bodyBytes]
/// using the `charset` parameter of the `Content-Type` header field, if
/// available. If it's unavailable or if the encoding name is unknown,
/// [LATIN1] is used by default, as per [RFC 2616][].
///
/// [RFC 2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
String
get
body
=>
_encodingForHeaders
(
headers
).
decode
(
bodyBytes
);
// https://html.spec.whatwg.org/#algorithm-for-extracting-a-character-encoding-from-a-meta-element
String
_getCharset
(
String
contentType
)
{
int
index
=
0
;
while
(
index
<
contentType
.
length
)
{
index
=
contentType
.
indexOf
(
new
RegExp
(
r'charset'
,
caseSensitive:
false
),
index
);
if
(
index
==
-
1
)
return
null
;
index
+=
7
;
index
=
_skipSpaces
(
contentType
,
index
);
if
(
index
>=
contentType
.
length
)
return
null
;
if
(
contentType
[
index
]
!=
'='
)
continue
;
index
+=
1
;
index
=
_skipSpaces
(
contentType
,
index
);
if
(
index
>=
contentType
.
length
)
return
null
;
String
delimiter
=
contentType
[
index
];
if
(
delimiter
==
'"'
||
delimiter
==
'
\'
'
)
{
index
+=
1
;
if
(
index
>=
contentType
.
length
)
return
null
;
int
start
=
index
;
int
end
=
contentType
.
indexOf
(
delimiter
,
start
);
if
(
end
==
-
1
)
return
null
;
return
contentType
.
substring
(
start
,
end
);
}
int
start
=
index
;
while
(
index
<
contentType
.
length
)
{
String
c
=
contentType
[
index
];
if
(
c
==
' '
||
c
==
';'
)
break
;
index
+=
1
;
}
return
contentType
.
substring
(
start
,
index
);
/// Creates a new HTTP response by waiting for the full body to become
/// available from a [StreamedResponse].
static
Future
<
Response
>
fromStream
(
StreamedResponse
response
)
{
return
response
.
stream
.
toBytes
().
then
((
List
<
int
>
body
)
{
return
new
Response
.
bytes
(
body
,
response
.
statusCode
,
request:
response
.
request
,
headers:
response
.
headers
,
isRedirect:
response
.
isRedirect
,
persistentConnection:
response
.
persistentConnection
,
reasonPhrase:
response
.
reasonPhrase
);
});
}
return
null
;
}
Encoding
_encodingForHeaders
(
Map
<
String
,
String
>
headers
)
{
if
(
headers
==
null
)
return
LATIN1
;
/// Returns the encoding to use for a response with the given headers. This
/// defaults to [LATIN1] if the headers don't specify a charset or
/// if that charset is unknown.
Encoding
_encodingForHeaders
(
Map
<
String
,
String
>
headers
)
=>
encodingForCharset
(
_contentTypeForHeaders
(
headers
).
parameters
[
'charset'
]);
/// Returns the [MediaType] object for the given headers's content-type.
///
/// Defaults to `application/octet-stream`.
MediaType
_contentTypeForHeaders
(
Map
<
String
,
String
>
headers
)
{
String
contentType
=
headers
[
'content-type'
];
if
(
contentType
==
null
)
return
LATIN1
;
String
charset
=
_getCharset
(
contentType
);
if
(
charset
==
null
)
return
LATIN1
;
return
Encoding
.
getByName
(
charset
)
??
LATIN1
;
if
(
contentType
!=
null
)
return
new
MediaType
.
parse
(
contentType
);
return
new
MediaType
(
"application"
,
"octet-stream"
);
}
packages/flutter/lib/src/http/streamed_request.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'byte_stream.dart'
;
import
'base_request.dart'
;
/// An HTTP request where the request body is sent asynchronously after the
/// connection has been established and the headers have been sent.
///
/// When the request is sent via [BaseClient.send], only the headers and
/// whatever data has already been written to [StreamedRequest.stream] will be
/// sent immediately. More data will be sent as soon as it's written to
/// [StreamedRequest.sink], and when the sink is closed the request will end.
class
StreamedRequest
extends
BaseRequest
{
/// Creates a new streaming request.
StreamedRequest
(
String
method
,
Uri
url
)
:
_controller
=
new
StreamController
<
List
<
int
>>(
sync:
true
),
super
(
method
,
url
);
/// The sink to which to write data that will be sent as the request body.
/// This may be safely written to before the request is sent; the data will be
/// buffered.
///
/// Closing this signals the end of the request.
EventSink
<
List
<
int
>>
get
sink
=>
_controller
.
sink
;
/// The controller for [sink], from which [BaseRequest] will read data for
/// [finalize].
final
StreamController
<
List
<
int
>>
_controller
;
/// Freezes all mutable fields other than [stream] and returns a
/// single-subscription [ByteStream] that emits the data being written to
/// [sink].
@override
ByteStream
finalize
()
{
super
.
finalize
();
return
new
ByteStream
(
_controller
.
stream
);
}
}
packages/flutter/lib/src/http/streamed_response.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'byte_stream.dart'
;
import
'base_response.dart'
;
import
'base_request.dart'
;
import
'utils.dart'
;
/// An HTTP response where the response body is received asynchronously after
/// the headers have been received.
class
StreamedResponse
extends
BaseResponse
{
/// The stream from which the response body data can be read. This should
/// always be a single-subscription stream.
final
ByteStream
stream
;
/// Creates a new streaming response. [stream] should be a single-subscription
/// stream.
StreamedResponse
(
Stream
<
List
<
int
>>
stream
,
int
statusCode
,
{
int
contentLength
,
BaseRequest
request
,
Map
<
String
,
String
>
headers:
const
<
String
,
String
>
{},
bool
isRedirect:
false
,
bool
persistentConnection:
true
,
String
reasonPhrase
})
:
this
.
stream
=
toByteStream
(
stream
),
super
(
statusCode
,
contentLength:
contentLength
,
request:
request
,
headers:
headers
,
isRedirect:
isRedirect
,
persistentConnection:
persistentConnection
,
reasonPhrase:
reasonPhrase
);
}
packages/flutter/lib/src/http/utils.dart
0 → 100644
View file @
400585cb
// Copyright (c) 2012, 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:async'
;
import
'dart:convert'
;
import
'dart:typed_data'
;
import
'byte_stream.dart'
;
/// Converts a [Map] from parameter names to values to a URL query string.
///
/// mapToQuery({"foo": "bar", "baz": "bang"});
/// //=> "foo=bar&baz=bang"
String
mapToQuery
(
Map
<
String
,
String
>
map
,
{
Encoding
encoding
})
{
List
<
List
<
String
>>
pairs
=
<
List
<
String
>>[];
map
.
forEach
((
String
key
,
String
value
)
=>
pairs
.
add
(<
String
>[
Uri
.
encodeQueryComponent
(
key
,
encoding:
encoding
),
Uri
.
encodeQueryComponent
(
value
,
encoding:
encoding
)]));
return
pairs
.
map
((
List
<
String
>
pair
)
=>
"
${pair[0]}
=
${pair[1]}
"
).
join
(
"&"
);
}
/// Like [String.split], but only splits on the first occurrence of the pattern.
/// This will always return an array of two elements or fewer.
///
/// split1("foo,bar,baz", ","); //=> ["foo", "bar,baz"]
/// split1("foo", ","); //=> ["foo"]
/// split1("", ","); //=> []
List
<
String
>
split1
(
String
toSplit
,
String
pattern
)
{
if
(
toSplit
.
isEmpty
)
return
<
String
>[];
int
index
=
toSplit
.
indexOf
(
pattern
);
if
(
index
==
-
1
)
return
<
String
>[
toSplit
];
return
<
String
>[
toSplit
.
substring
(
0
,
index
),
toSplit
.
substring
(
index
+
pattern
.
length
)
];
}
/// Returns the [Encoding] that corresponds to [charset]. Returns [fallback] if
/// [charset] is null or if no [Encoding] was found that corresponds to
/// [charset].
Encoding
encodingForCharset
(
String
charset
,
[
Encoding
fallback
=
LATIN1
])
{
if
(
charset
==
null
)
return
fallback
;
Encoding
encoding
=
Encoding
.
getByName
(
charset
);
return
encoding
==
null
?
fallback
:
encoding
;
}
/// Returns the [Encoding] that corresponds to [charset]. Throws a
/// [FormatException] if no [Encoding] was found that corresponds to [charset].
/// [charset] may not be null.
Encoding
requiredEncodingForCharset
(
String
charset
)
{
Encoding
encoding
=
Encoding
.
getByName
(
charset
);
if
(
encoding
!=
null
)
return
encoding
;
throw
new
FormatException
(
'Unsupported encoding "
$charset
".'
);
}
/// A regular expression that matches strings that are composed entirely of
/// ASCII-compatible characters.
final
RegExp
_kAsciiOnly
=
new
RegExp
(
r"^[\x00-\x7F]+$"
);
/// Returns whether [string] is composed entirely of ASCII-compatible
/// characters.
bool
isPlainAscii
(
String
string
)
=>
_kAsciiOnly
.
hasMatch
(
string
);
/// Converts [input] into a [Uint8List].
///
/// If [input] is a [TypedData], this just returns a view on [input].
Uint8List
toUint8List
(
dynamic
input
)
{
if
(
input
is
Uint8List
)
return
input
;
if
(
input
is
TypedData
)
return
new
Uint8List
.
view
(
input
.
buffer
);
return
new
Uint8List
.
fromList
(
input
);
}
/// If [stream] is already a [ByteStream], returns it. Otherwise, wraps it in a
/// [ByteStream].
ByteStream
toByteStream
(
Stream
<
List
<
int
>>
stream
)
{
if
(
stream
is
ByteStream
)
return
stream
;
return
new
ByteStream
(
stream
);
}
/// Calls [onDone] once [stream] (a single-subscription [Stream]) is finished.
/// The return value, also a single-subscription [Stream] should be used in
/// place of [stream] after calling this method.
Stream
/*<T>*/
onDone
/*<T>*/
(
Stream
/*<T>*/
stream
,
void
onDone
())
=>
stream
.
transform
(
new
StreamTransformer
.
fromHandlers
(
handleDone:
(
EventSink
<
dynamic
>
sink
)
{
// ignore: always_specify_types
sink
.
close
();
onDone
();
}));
// TODO(nweiz): remove this when issue 7786 is fixed.
/// Pipes all data and errors from [stream] into [sink]. When [stream] is done,
/// [sink] is closed and the returned [Future] is completed.
Future
<
dynamic
>
store
(
Stream
<
dynamic
>
stream
,
EventSink
<
dynamic
>
sink
)
{
Completer
<
dynamic
>
completer
=
new
Completer
<
dynamic
>();
stream
.
listen
(
sink
.
add
,
onError:
sink
.
addError
,
onDone:
()
{
sink
.
close
();
completer
.
complete
();
});
return
completer
.
future
;
}
/// Pipes all data and errors from [stream] into [sink]. Completes [Future] once
/// [stream] is done. Unlike [store], [sink] remains open after [stream] is
/// done.
Future
<
dynamic
>
writeStreamToSink
(
Stream
<
dynamic
>
stream
,
EventSink
<
dynamic
>
sink
)
{
Completer
<
dynamic
>
completer
=
new
Completer
<
dynamic
>();
stream
.
listen
(
sink
.
add
,
onError:
sink
.
addError
,
onDone:
()
=>
completer
.
complete
());
return
completer
.
future
;
}
/// A pair of values.
class
Pair
<
E
,
F
>
{
E
first
;
F
last
;
Pair
(
this
.
first
,
this
.
last
);
@override
String
toString
()
=>
'(
$first
,
$last
)'
;
@override
bool
operator
==(
dynamic
other
)
{
if
(
other
is
!
Pair
)
return
false
;
return
other
.
first
==
first
&&
other
.
last
==
last
;
}
@override
int
get
hashCode
=>
first
.
hashCode
^
last
.
hashCode
;
}
/// Configures [future] so that its result (success or exception) is passed on
/// to [completer].
void
chainToCompleter
(
Future
<
dynamic
>
future
,
Completer
<
dynamic
>
completer
)
{
future
.
then
(
completer
.
complete
,
onError:
completer
.
completeError
);
}
packages/flutter/lib/src/services/asset_bundle.dart
View file @
400585cb
...
...
@@ -13,8 +13,6 @@ import 'package:flutter/http.dart' as http;
import
'package:mojo/core.dart'
as
core
;
import
'package:mojo_services/mojo/asset_bundle/asset_bundle.mojom.dart'
as
mojom
;
import
'shell.dart'
;
/// A collection of resources used by the application.
///
/// Asset bundles contain resources, such as images and strings, that can be
...
...
@@ -86,12 +84,18 @@ class NetworkAssetBundle extends AssetBundle {
@override
Future
<
core
.
MojoDataPipeConsumer
>
load
(
String
key
)
async
{
return
await
http
.
readDataPipe
(
_urlFromKey
(
key
));
http
.
Response
response
=
await
http
.
get
(
_urlFromKey
(
key
));
if
(
response
.
statusCode
==
200
)
return
null
;
core
.
MojoDataPipe
pipe
=
new
core
.
MojoDataPipe
();
core
.
DataPipeFiller
.
fillHandle
(
pipe
.
producer
,
response
.
bodyBytes
.
buffer
.
asByteData
());
return
pipe
.
consumer
;
}
@override
Future
<
String
>
loadString
(
String
key
,
{
bool
cache:
true
})
async
{
return
(
await
http
.
get
(
_urlFromKey
(
key
))).
body
;
http
.
Response
response
=
await
http
.
get
(
_urlFromKey
(
key
));
return
response
.
statusCode
==
200
?
response
.
body
:
null
;
}
/// Retrieve a string from the asset bundle, parse it with the given function,
...
...
@@ -191,22 +195,6 @@ class MojoAssetBundle extends CachingAssetBundle {
/// Creates an [AssetBundle] interface around the given [mojom.AssetBundleProxy] Mojo service.
MojoAssetBundle
(
this
.
_bundle
);
/// Retrieves the asset bundle located at the given URL, unpacks it, and provides it contents.
factory
MojoAssetBundle
.
fromNetwork
(
String
relativeUrl
)
{
final
mojom
.
AssetBundleProxy
bundle
=
new
mojom
.
AssetBundleProxy
.
unbound
();
_fetchAndUnpackBundleAsychronously
(
relativeUrl
,
bundle
);
return
new
MojoAssetBundle
(
bundle
);
}
static
Future
<
Null
>
_fetchAndUnpackBundleAsychronously
(
String
relativeUrl
,
mojom
.
AssetBundleProxy
bundle
)
async
{
final
core
.
MojoDataPipeConsumer
bundleData
=
await
http
.
readDataPipe
(
Uri
.
base
.
resolve
(
relativeUrl
));
final
mojom
.
AssetUnpackerProxy
unpacker
=
shell
.
connectToApplicationService
(
'mojo:asset_bundle'
,
mojom
.
AssetUnpacker
.
connectToService
);
unpacker
.
unpackZipStream
(
bundleData
,
bundle
);
unpacker
.
close
();
}
mojom
.
AssetBundleProxy
_bundle
;
@override
...
...
packages/flutter/lib/src/services/image_provider.dart
View file @
400585cb
...
...
@@ -5,6 +5,7 @@
import
'dart:async'
;
import
'dart:ui'
show
Size
,
Locale
,
hashValues
;
import
'dart:ui'
as
ui
show
Image
;
import
'dart:typed_data'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/http.dart'
as
http
;
...
...
@@ -278,7 +279,7 @@ abstract class DataPipeImageProvider<T> extends ImageProvider<T> {
// TODO(ianh): Find some way to honour cache headers to the extent that when the
// last reference to an image is released, we proactively evict the image from
// our cache if the headers describe the image as having expired at that point.
class
NetworkImage
extends
DataPipe
ImageProvider
<
NetworkImage
>
{
class
NetworkImage
extends
ImageProvider
<
NetworkImage
>
{
/// Creates an object that fetches the image at the given URL.
///
/// The arguments must not be null.
...
...
@@ -296,15 +297,36 @@ class NetworkImage extends DataPipeImageProvider<NetworkImage> {
}
@override
Future
<
mojo
.
MojoDataPipeConsumer
>
loadDataPipe
(
NetworkImage
key
)
async
{
assert
(
key
==
this
);
return
http
.
readDataPipe
(
Uri
.
base
.
resolve
(
key
.
url
));
ImageStreamCompleter
load
(
NetworkImage
key
)
{
return
new
OneFrameImageStreamCompleter
(
_loadAsync
(
key
),
informationCollector:
(
StringBuffer
information
)
{
information
.
writeln
(
'Image provider:
$this
'
);
information
.
write
(
'Image key:
$key
'
);
}
);
}
@override
double
getScale
(
NetworkImage
key
)
{
Future
<
ImageInfo
>
_loadAsync
(
NetworkImage
key
)
async
{
assert
(
key
==
this
);
return
key
.
scale
;
final
Uri
resolved
=
Uri
.
base
.
resolve
(
key
.
url
);
final
http
.
Response
response
=
await
http
.
get
(
resolved
);
if
(
response
==
null
||
response
.
statusCode
!=
200
)
return
null
;
Uint8List
bytes
=
response
.
bodyBytes
;
if
(
bytes
.
lengthInBytes
==
0
)
return
null
;
final
ui
.
Image
image
=
await
decodeImageFromList
(
bytes
);
if
(
image
==
null
)
return
null
;
return
new
ImageInfo
(
image:
image
,
scale:
key
.
scale
,
);
}
@override
...
...
packages/flutter/pubspec.yaml
View file @
400585cb
...
...
@@ -10,6 +10,11 @@ dependencies:
meta
:
^1.0.3
vector_math
:
'
>=2.0.3
<3.0.0'
# async and http_parser can be removed when we move to using dart-lang/http
# directly.
async
:
"
^1.10.0"
http_parser
:
"
>=0.0.1
<4.0.0"
sky_engine
:
path
:
../../bin/cache/pkg/sky_engine
sky_services
:
...
...
packages/flutter_test/lib/src/binding.dart
View file @
400585cb
...
...
@@ -8,6 +8,7 @@ import 'dart:ui' as ui;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/http.dart'
as
http
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/services.dart'
;
...
...
@@ -109,6 +110,13 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
@override
void
initInstances
()
{
timeDilation
=
1.0
;
// just in case the developer has artificially changed it for development
http
.
Client
.
clientOverride
=
()
{
return
new
http
.
MockClient
((
http
.
Request
request
){
return
new
Future
<
http
.
Response
>.
value
(
new
http
.
Response
(
"Mocked: Unavailable."
,
404
,
request:
request
)
);
});
};
super
.
initInstances
();
}
...
...
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