mojo_client.dart 7.15 KB
Newer Older
1 2 3 4 5 6 7 8
// 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';

9
import 'package:flutter/services.dart';
10 11
import 'package:mojo_services/mojo/network_service.mojom.dart' as mojo;
import 'package:mojo_services/mojo/url_loader.mojom.dart' as mojo;
12 13 14
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;
15
import 'package:mojo/mojo/http_header.mojom.dart' as mojo;
16 17 18

import 'response.dart';

Florian Loitsch's avatar
Florian Loitsch committed
19
/// A `mojo`-based HTTP client.
20 21
class MojoClient {

22 23 24 25 26
  /// Sends an HTTP HEAD request with the given headers to the given URL, which
  /// can be a [Uri] or a [String].
  Future<Response> head(dynamic url, { Map<String, String> headers }) {
    return _send("HEAD", url, headers);
  }
27

28 29 30 31 32
  /// Sends an HTTP GET request with the given headers to the given URL, which can
  /// be a [Uri] or a [String].
  Future<Response> get(dynamic url, { Map<String, String> headers }) {
    return _send("GET", url, headers);
  }
33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
  /// 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].
  Future<Response> post(dynamic url, { Map<String, String> headers, dynamic body, Encoding encoding: UTF8 }) {
    return _send("POST", url, headers, body, encoding);
  }
53

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
  /// 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].
  Future<Response> put(dynamic url, { Map<String, String> headers, dynamic body, Encoding encoding: UTF8 }) {
    return _send("PUT", url, headers, body, encoding);
  }
73

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
  /// 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].
  Future<Response> patch(dynamic url, {Map<String, String> headers, dynamic body, Encoding encoding: UTF8 }) {
    return _send("PATCH", url, headers, body, encoding);
  }
93

94 95 96 97 98
  /// Sends an HTTP DELETE request with the given headers to the given URL, which
  /// can be a [Uri] or a [String].
  Future<Response> delete(dynamic url, { Map<String, String> headers }) {
    return _send("DELETE", url, headers);
  }
99

100 101 102 103 104 105 106 107
  /// 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.
  Future<String> read(dynamic url, { Map<String, String> headers }) {
    return get(url, headers: headers).then((Response response) {
108 109 110 111 112
      _checkResponseSuccess(url, response);
      return response.body;
    });
  }

113 114 115 116 117 118 119
  /// 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.
  Future<Uint8List> readBytes(dynamic url, { Map<String, String> headers }) {
120
    return get(url, headers: headers).then((Response response) {
121 122 123 124 125
      _checkResponseSuccess(url, response);
      return response.bodyBytes;
    });
  }

126
  Future<Response> _send(String method, dynamic url, Map<String, String> headers, [dynamic body, Encoding encoding = UTF8]) async {
127
    mojo.UrlLoaderProxy loader = new mojo.UrlLoaderProxy.unbound();
128
    List<mojo.HttpHeader> mojoHeaders = <mojo.HttpHeader>[];
129
    headers?.forEach((String name, String value) {
130 131 132 133 134
      mojo.HttpHeader header = new mojo.HttpHeader()
        ..name = name
        ..value = value;
      mojoHeaders.add(header);
    });
135 136
    mojo.UrlRequest request = new mojo.UrlRequest()
      ..url = url.toString()
137
      ..headers = mojoHeaders
138 139 140 141
      ..method = method;
    if (body != null) {
      mojo.MojoDataPipe pipe = new mojo.MojoDataPipe();
      request.body = <mojo.MojoDataPipeConsumer>[pipe.consumer];
142
      Uint8List encodedBody = encoding.encode(body);
143
      ByteData data = new ByteData.view(encodedBody.buffer);
144 145
      mojo.DataPipeFiller.fillHandle(pipe.producer, data);
    }
146
    try {
147
      networkService.ptr.createUrlLoader(loader);
148 149
      mojo.UrlResponse response = (await loader.ptr.start(request)).response;
      ByteData data = await mojo.DataPipeDrainer.drainHandle(response.body);
150
      Uint8List bodyBytes = new Uint8List.view(data.buffer);
151 152
      String bodyString = new String.fromCharCodes(bodyBytes);
      return new Response(body: bodyString, bodyBytes: bodyBytes, statusCode: response.statusCode);
153 154
    } catch (e) {
      print("NetworkService unavailable $e");
155
      return new Response(statusCode: 500);
156 157 158 159 160
    } finally {
      loader.close();
    }
  }

161
  void _checkResponseSuccess(dynamic url, Response response) {
162 163 164
    if (response.statusCode < 400)
      return;
    throw new Exception("Request to $url failed with status ${response.statusCode}.");
165 166
  }

167 168 169 170 171 172 173
  static mojo.NetworkServiceProxy _initNetworkService() {
    mojo.NetworkServiceProxy proxy = new mojo.NetworkServiceProxy.unbound();
    shell.connectToService("mojo:authenticated_network_service", proxy);
    return proxy;
  }

  static final mojo.NetworkServiceProxy networkService = _initNetworkService();
174
}