Commit 055b6426 authored by Collin Jackson's avatar Collin Jackson

Merge pull request #1938 from collinjackson/http

Bare bones Mojo reimplementation of Dart’s http package
parents 218bbdc1 35368999
// Copyright 2015 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.
/// Service exposed to Flutter apps that implements a subset of Dart's
/// http package API.
///
/// This library will probably be moved into a separate package eventually.
///
/// This library depends only on core Dart libraries as well as the `mojo`,
/// `mojo_services`, and `sky_services` and packages.
library http;
export 'src/http/http.dart';
export 'src/http/response.dart';
// 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.
/// A [Future]-based library for making HTTP requests. It's based on
/// Dart's `http` package, but we've removed the dependency on mirrors
/// and added a `mojo`-based HTTP client.
library http;
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'mojo_client.dart';
import 'response.dart';
/// Sends an HTTP HEAD request with the given headers to the given URL, which
/// can be a [Uri] or a [String].
///
/// 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<Response> head(url) =>
_withClient((client) => client.head(url));
/// Sends an HTTP GET request with the given headers to the given URL, which can
/// be a [Uri] or a [String].
///
/// 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<Response> get(url) =>
_withClient((client) => client.get(url));
/// 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.
Future<Response> post(url, {body}) =>
_withClient((client) => client.post(url, body: body));
/// 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".
Future<Response> put(url, {String body}) =>
_withClient((client) => client.put(url, body: body));
/// 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 [Request] or
/// [StreamedRequest] instead.
Future<Response> patch(url, { body }) =>
_withClient((client) => client.patch(url, body: body));
/// Sends an HTTP DELETE request with the given headers to the given URL, which
/// can be a [Uri] or a [String].
///
/// 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.
///
/// For more fine-grained control over the request, use [Request] instead.
Future<Response> delete(url) =>
_withClient((client) => client.delete(url));
/// 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.
///
/// 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.
///
/// For more fine-grained control over the request and response, use [Request]
/// instead.
Future<String> read(url) =>
_withClient((client) => client.read(url));
/// 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.
///
/// 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.
///
/// For more fine-grained control over the request and response, use [Request]
/// instead.
Future<Uint8List> readBytes(url) =>
_withClient((client) => client.readBytes(url));
Future _withClient(Future fn(MojoClient)) {
var client = new MojoClient();
var future = fn(client);
return future.whenComplete(client.close);
}
// 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.
library base_client;
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:mojo/core.dart' as mojo;
import 'package:mojo/mojo/url_request.mojom.dart' as mojo;
import 'package:mojo/mojo/url_response.mojom.dart' as mojo;
import 'package:mojo_services/mojo/network_service.mojom.dart' as mojo;
import 'package:mojo_services/mojo/url_loader.mojom.dart' as mojo;
import 'package:flutter/src/services/shell.dart';
import 'response.dart';
mojo.NetworkServiceProxy _initNetworkService() {
mojo.NetworkServiceProxy networkService = new mojo.NetworkServiceProxy.unbound();
shell.connectToService("mojo:authenticated_network_service", networkService);
return networkService;
}
final mojo.NetworkServiceProxy _networkService = _initNetworkService();
/// A `mojo`-based HTTP client
class MojoClient {
Future<Response> head(url, {Map<String, String> headers}) =>
_send("HEAD", url, headers);
Future<Response> get(url, {Map<String, String> headers}) =>
_send("GET", url, headers);
Future<Response> post(url, {Map<String, String> headers, body,
Encoding encoding}) =>
_send("POST", url, headers, body, encoding);
Future<Response> put(url, {Map<String, String> headers, body,
Encoding encoding}) =>
_send("PUT", url, headers, body, encoding);
Future<Response> patch(url, {Map<String, String> headers, body,
Encoding encoding}) =>
_send("PATCH", url, headers, body, encoding);
Future<Response> delete(url, {Map<String, String> headers}) =>
_send("DELETE", url, headers);
Future<String> read(url, {Map<String, String> headers}) {
return get(url, headers: headers).then((response) {
_checkResponseSuccess(url, response);
return response.body;
});
}
Future<Uint8List> readBytes(url, {Map<String, String> headers}) {
return get(url, headers: headers).then((response) {
_checkResponseSuccess(url, response);
return response.bodyBytes;
});
}
Future<Response> _send(String method, url,
Map<String, String> headers, [body, Encoding encoding]) async {
mojo.UrlLoaderProxy loader = new mojo.UrlLoaderProxy.unbound();
mojo.UrlRequest request = new mojo.UrlRequest()
..url = url.toString()
..method = method
..body = body;
try {
_networkService.ptr.createUrlLoader(loader);
mojo.UrlResponse response = (await loader.ptr.start(request)).response;
ByteData data = await mojo.DataPipeDrainer.drainHandle(response.body);
Uint8List bodyAsList = new Uint8List.view(data.buffer);
String bodyAsString = new String.fromCharCodes(bodyAsList);
return new Response(bodyAsString, response.statusCode);
} catch (e) {
print("NetworkService unavailable $e");
return new Response(null, 500);
} finally {
loader.close();
}
}
void _checkResponseSuccess(url, Response response) {
if (response.statusCode < 400) return;
var 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);
}
void close() {}
}
// 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.
library response;
/// An HTTP response where the entire response body is known in advance.
class Response {
const Response(this.body, this.statusCode);
final String body;
final int statusCode;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment