Line data Source code
1 : // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import 'dart:async';
6 : import 'dart:convert';
7 : import 'dart:typed_data';
8 :
9 : import 'package:collection/collection.dart';
10 :
11 : import 'base_request.dart';
12 : import 'client.dart';
13 : import 'exception.dart';
14 : import 'request.dart';
15 : import 'response.dart';
16 : import 'streamed_response.dart';
17 :
18 : /// The abstract base class for an HTTP client. This is a mixin-style class;
19 : /// subclasses only need to implement [send] and maybe [close], and then they
20 : /// get various convenience methods for free.
21 : abstract class BaseClient implements Client {
22 : /// Sends an HTTP HEAD request with the given headers to the given URL, which
23 : /// can be a [Uri] or a [String].
24 : ///
25 : /// For more fine-grained control over the request, use [send] instead.
26 : Future<Response> head(url, {Map<String, String> headers}) =>
27 0 : _sendUnstreamed("HEAD", url, headers);
28 :
29 : /// Sends an HTTP GET request with the given headers to the given URL, which
30 : /// can be a [Uri] or a [String].
31 : ///
32 : /// For more fine-grained control over the request, use [send] instead.
33 : Future<Response> get(url, {Map<String, String> headers}) =>
34 0 : _sendUnstreamed("GET", url, headers);
35 :
36 : /// Sends an HTTP POST request with the given headers and body to the given
37 : /// URL, which can be a [Uri] or a [String].
38 : ///
39 : /// [body] sets the body of the request. It can be a [String], a [List<int>]
40 : /// or a [Map<String, String>]. If it's a String, it's encoded using
41 : /// [encoding] and used as the body of the request. The content-type of the
42 : /// request will default to "text/plain".
43 : ///
44 : /// If [body] is a List, it's used as a list of bytes for the body of the
45 : /// request.
46 : ///
47 : /// If [body] is a Map, it's encoded as form fields using [encoding]. The
48 : /// content-type of the request will be set to
49 : /// `"application/x-www-form-urlencoded"`; this cannot be overridden.
50 : ///
51 : /// [encoding] defaults to UTF-8.
52 : ///
53 : /// For more fine-grained control over the request, use [send] instead.
54 : Future<Response> post(url, {Map<String, String> headers, body,
55 : Encoding encoding}) =>
56 0 : _sendUnstreamed("POST", url, headers, body, encoding);
57 :
58 : /// Sends an HTTP PUT request with the given headers and body to the given
59 : /// URL, which can be a [Uri] or a [String].
60 : ///
61 : /// [body] sets the body of the request. It can be a [String], a [List<int>]
62 : /// or a [Map<String, String>]. If it's a String, it's encoded using
63 : /// [encoding] and used as the body of the request. The content-type of the
64 : /// request will default to "text/plain".
65 : ///
66 : /// If [body] is a List, it's used as a list of bytes for the body of the
67 : /// request.
68 : ///
69 : /// If [body] is a Map, it's encoded as form fields using [encoding]. The
70 : /// content-type of the request will be set to
71 : /// `"application/x-www-form-urlencoded"`; this cannot be overridden.
72 : ///
73 : /// [encoding] defaults to UTF-8.
74 : ///
75 : /// For more fine-grained control over the request, use [send] instead.
76 : Future<Response> put(url, {Map<String, String> headers, body,
77 : Encoding encoding}) =>
78 0 : _sendUnstreamed("PUT", url, headers, body, encoding);
79 :
80 : /// Sends an HTTP PATCH request with the given headers and body to the given
81 : /// URL, which can be a [Uri] or a [String].
82 : ///
83 : /// [body] sets the body of the request. It can be a [String], a [List<int>]
84 : /// or a [Map<String, String>]. If it's a String, it's encoded using
85 : /// [encoding] and used as the body of the request. The content-type of the
86 : /// request will default to "text/plain".
87 : ///
88 : /// If [body] is a List, it's used as a list of bytes for the body of the
89 : /// request.
90 : ///
91 : /// If [body] is a Map, it's encoded as form fields using [encoding]. The
92 : /// content-type of the request will be set to
93 : /// `"application/x-www-form-urlencoded"`; this cannot be overridden.
94 : ///
95 : /// [encoding] defaults to UTF-8.
96 : ///
97 : /// For more fine-grained control over the request, use [send] instead.
98 : Future<Response> patch(url, {Map<String, String> headers, body,
99 : Encoding encoding}) =>
100 0 : _sendUnstreamed("PATCH", url, headers, body, encoding);
101 :
102 : /// Sends an HTTP DELETE request with the given headers to the given URL,
103 : /// which can be a [Uri] or a [String].
104 : ///
105 : /// For more fine-grained control over the request, use [send] instead.
106 : Future<Response> delete(url, {Map<String, String> headers}) =>
107 0 : _sendUnstreamed("DELETE", url, headers);
108 :
109 : /// Sends an HTTP GET request with the given headers to the given URL, which
110 : /// can be a [Uri] or a [String], and returns a Future that completes to the
111 : /// body of the response as a String.
112 : ///
113 : /// The Future will emit a [ClientException] if the response doesn't have a
114 : /// success status code.
115 : ///
116 : /// For more fine-grained control over the request and response, use [send] or
117 : /// [get] instead.
118 : Future<String> read(url, {Map<String, String> headers}) {
119 0 : return get(url, headers: headers).then((response) {
120 0 : _checkResponseSuccess(url, response);
121 0 : return response.body;
122 : });
123 : }
124 :
125 : /// Sends an HTTP GET request with the given headers to the given URL, which
126 : /// can be a [Uri] or a [String], and returns a Future that completes to the
127 : /// body of the response as a list of bytes.
128 : ///
129 : /// The Future will emit an [ClientException] if the response doesn't have a
130 : /// success status code.
131 : ///
132 : /// For more fine-grained control over the request and response, use [send] or
133 : /// [get] instead.
134 : Future<Uint8List> readBytes(url, {Map<String, String> headers}) {
135 0 : return get(url, headers: headers).then((response) {
136 0 : _checkResponseSuccess(url, response);
137 0 : return response.bodyBytes;
138 : });
139 : }
140 :
141 : /// Sends an HTTP request and asynchronously returns the response.
142 : ///
143 : /// Implementers should call [BaseRequest.finalize] to get the body of the
144 : /// request as a [ByteStream]. They shouldn't make any assumptions about the
145 : /// state of the stream; it could have data written to it asynchronously at a
146 : /// later point, or it could already be closed when it's returned. Any
147 : /// internal HTTP errors should be wrapped as [ClientException]s.
148 : Future<StreamedResponse> send(BaseRequest request);
149 :
150 : /// Sends a non-streaming [Request] and returns a non-streaming [Response].
151 : Future<Response> _sendUnstreamed(String method, url,
152 : Map<String, String> headers, [body, Encoding encoding]) async {
153 :
154 0 : if (url is String) url = Uri.parse(url);
155 0 : var request = new Request(method, url);
156 :
157 0 : if (headers != null) request.headers.addAll(headers);
158 0 : if (encoding != null) request.encoding = encoding;
159 : if (body != null) {
160 0 : if (body is String) {
161 0 : request.body = body;
162 0 : } else if (body is List) {
163 0 : request.bodyBytes = DelegatingList.typed(body);
164 0 : } else if (body is Map) {
165 0 : request.bodyFields = DelegatingMap.typed(body);
166 : } else {
167 0 : throw new ArgumentError('Invalid request body "$body".');
168 : }
169 : }
170 :
171 0 : return Response.fromStream(await send(request));
172 0 : }
173 :
174 : /// Throws an error if [response] is not successful.
175 : void _checkResponseSuccess(url, Response response) {
176 0 : if (response.statusCode < 400) return;
177 0 : var message = "Request to $url failed with status ${response.statusCode}";
178 0 : if (response.reasonPhrase != null) {
179 0 : message = "$message: ${response.reasonPhrase}";
180 : }
181 0 : if (url is String) url = Uri.parse(url);
182 0 : throw new ClientException("$message.", url);
183 : }
184 :
185 : /// Closes the client and cleans up any resources associated with it. It's
186 : /// important to close each client when it's done being used; failing to do so
187 : /// can cause the Dart process to hang.
188 : void close() {}
189 : }
|