Skip to content

Commit a6d2371

Browse files
committed
fix: broken unit-test, typo in readme
1 parent 592f5b8 commit a6d2371

File tree

7 files changed

+4660
-3577
lines changed

7 files changed

+4660
-3577
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
## 1.0.0
22

33
- Initial version
4+
5+
## 3.0.1
6+
7+
- updated api with breaking changes
8+
9+
## 3.0.3
10+
11+
- fix: parse json only if requested

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
![banner-01](https://user-images.githubusercontent.com/1287098/67152735-969fd300-f2e5-11e9-9aa5-b73652ac502e.png)
1+
![banner-01](https://user-images.githubusercontent.com/1287098/68531964-ccead400-0320-11ea-93a6-fa83b9183dd8.png)
22

3-
# Flutter HTTP client + json + cookies.
43

5-
a flutter library to simply call HTTP requests (inspired by python [requests](https://github.com/psf/requests) module). It comes with JSON support and a lightweight implementation to store cookies like a browser.
4+
a flutter library to make HTTP requests (inspired by python [requests](https://github.com/psf/requests) module). It comes with JSON support and a lightweight implementation to store cookies like a browser.
65

76
### Cookies, huh?
87
Server side cookies (via response header `SET-COOKIE`) are stored using the assistance of `shared_preferences`. Stored cookies will be send seamlessly on the next http requests you make to the same domain (simple implementation, similar to a web browser)
@@ -14,7 +13,7 @@ Add this to your package's pubspec.yaml file:
1413

1514
```yaml
1615
dependencies:
17-
requests: ^3.0.1
16+
requests: ^3.0.3
1817
```
1918
2019
## Usage
@@ -61,7 +60,7 @@ just like in python's request module, the `Response` object has this functionall
6160

6261
### Class Methods
6362

64-
- `.getHostnam(url)` - returns the hostname of the given url
63+
- `.getHostname(url)` - returns the hostname of the given url
6564
- `.clearStoredCookies(hostname)` - clears the stored cookies for the hostname
6665
- `.setStoredCookies(hostname, Map<String, String>)` - set the stored cookies for the hostname
6766
- `.getStoredCookies(hostname)` - returns a Map<String, String> of the stored cookies for the hostname

lib/src/common.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,12 @@ class Common {
4646
var digest = sha256.convert(bytes);
4747
return toHexString(digest.bytes);
4848
}
49+
50+
static String encodeMap(Map data) {
51+
return data.keys.map((key) {
52+
var k = Uri.encodeComponent(key.toString());
53+
var v = Uri.encodeComponent(data[key].toString());
54+
return "${k}=${v}";
55+
}).join("&");
56+
}
4957
}

lib/src/requests.dart

Lines changed: 43 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'common.dart';
99
import 'event.dart';
1010
import 'dart:io';
1111

12-
enum RequestBodyEncoding { JSON, FormURLEncoded }
12+
enum RequestBodyEncoding { JSON, FormURLEncoded, PlainText }
1313

1414
final Logger log = Logger('requests');
1515

@@ -28,8 +28,7 @@ class Response {
2828

2929
throwForStatus() {
3030
if (!success) {
31-
throw HTTPException(
32-
"Invalid HTTP status code $statusCode for url ${url}", this);
31+
throw HTTPException("Invalid HTTP status code $statusCode for url ${url}", this);
3332
}
3433
}
3534

@@ -69,31 +68,17 @@ class Requests {
6968
static const String HTTP_METHOD_HEAD = "head";
7069
static const int DEFAULT_TIMEOUT_SECONDS = 10;
7170

72-
static const RequestBodyEncoding DEFAULT_BODY_ENCODING =
73-
RequestBodyEncoding.FormURLEncoded;
71+
static const RequestBodyEncoding DEFAULT_BODY_ENCODING = RequestBodyEncoding.FormURLEncoded;
7472

75-
static Set _cookiesKeysToIgnore = Set.from([
76-
"SameSite",
77-
"Path",
78-
"Domain",
79-
"Max-Age",
80-
"Expires",
81-
"Secure",
82-
"HttpOnly"
83-
]);
73+
static Set _cookiesKeysToIgnore = Set.from(["SameSite", "Path", "Domain", "Max-Age", "Expires", "Secure", "HttpOnly"]);
8474

8575
static Map<String, String> _extractResponseCookies(responseHeaders) {
8676
Map<String, String> cookies = {};
8777
for (var key in responseHeaders.keys) {
8878
if (Common.equalsIgnoreCase(key, 'set-cookie')) {
8979
String cookie = responseHeaders[key];
9080
cookie.split(",").forEach((String one) {
91-
cookie
92-
.split(";")
93-
.map((x) => x.trim().split("="))
94-
.where((x) => x.length == 2)
95-
.where((x) => !_cookiesKeysToIgnore.contains(x[0]))
96-
.forEach((x) => cookies[x[0]] = x[1]);
81+
cookie.split(";").map((x) => x.trim().split("=")).where((x) => x.length == 2).where((x) => !_cookiesKeysToIgnore.contains(x[0])).forEach((x) => cookies[x[0]] = x[1]);
9782
});
9883
break;
9984
}
@@ -102,11 +87,9 @@ class Requests {
10287
return cookies;
10388
}
10489

105-
static Future<Map> _constructRequestHeaders(
106-
String hostname, Map<String, String> customHeaders) async {
90+
static Future<Map> _constructRequestHeaders(String hostname, Map<String, String> customHeaders) async {
10791
var cookies = await getStoredCookies(hostname);
108-
String cookie =
109-
cookies.keys.map((key) => "$key=${cookies[key]}").join("; ");
92+
String cookie = cookies.keys.map((key) => "$key=${cookies[key]}").join("; ");
11093
Map<String, String> requestHeaders = Map();
11194
requestHeaders['cookie'] = cookie;
11295

@@ -123,14 +106,12 @@ class Requests {
123106
var cookies = Common.fromJson(cookiesJson);
124107
return Map<String, String>.from(cookies);
125108
} catch (e) {
126-
log.shout(
127-
"problem reading stored cookies. fallback with empty cookies $e");
109+
log.shout("problem reading stored cookies. fallback with empty cookies $e");
128110
return Map<String, String>();
129111
}
130112
}
131113

132-
static Future setStoredCookies(
133-
String hostname, Map<String, String> cookies) async {
114+
static Future setStoredCookies(String hostname, Map<String, String> cookies) async {
134115
String hostnameHash = Common.hashStringSHA256(hostname);
135116
String cookiesJson = Common.toJson(cookies);
136117
await Common.storageSet('cookies-$hostnameHash', cookiesJson);
@@ -146,8 +127,7 @@ class Requests {
146127
return "${uri.host}:${uri.port}";
147128
}
148129

149-
static Future<Response> _handleHttpResponse(
150-
String hostname, http.Response rawResponse, bool persistCookies) async {
130+
static Future<Response> _handleHttpResponse(String hostname, http.Response rawResponse, bool persistCookies) async {
151131
if (persistCookies) {
152132
var responseCookies = _extractResponseCookies(rawResponse.headers);
153133
if (responseCookies.isNotEmpty) {
@@ -166,106 +146,31 @@ class Requests {
166146
return response;
167147
}
168148

169-
static Future<Response> head(String url,
170-
{headers,
171-
bodyEncoding = DEFAULT_BODY_ENCODING,
172-
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
173-
persistCookies = true,
174-
verify = true}) {
175-
return _httpRequest(HTTP_METHOD_HEAD, url,
176-
bodyEncoding: bodyEncoding,
177-
headers: headers,
178-
timeoutSeconds: timeoutSeconds,
179-
persistCookies: persistCookies,
180-
verify: verify);
149+
static Future<Response> head(String url, {headers, bodyEncoding = DEFAULT_BODY_ENCODING, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, persistCookies = true, verify = true}) {
150+
return _httpRequest(HTTP_METHOD_HEAD, url, bodyEncoding: bodyEncoding, headers: headers, timeoutSeconds: timeoutSeconds, persistCookies: persistCookies, verify: verify);
181151
}
182152

183-
static Future<Response> get(String url,
184-
{headers,
185-
bodyEncoding = DEFAULT_BODY_ENCODING,
186-
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
187-
persistCookies = true,
188-
verify = true}) {
189-
return _httpRequest(HTTP_METHOD_GET, url,
190-
bodyEncoding: bodyEncoding,
191-
headers: headers,
192-
timeoutSeconds: timeoutSeconds,
193-
persistCookies: persistCookies,
194-
verify: verify);
153+
static Future<Response> get(String url, {headers, bodyEncoding = DEFAULT_BODY_ENCODING, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, persistCookies = true, verify = true}) {
154+
return _httpRequest(HTTP_METHOD_GET, url, bodyEncoding: bodyEncoding, headers: headers, timeoutSeconds: timeoutSeconds, persistCookies: persistCookies, verify: verify);
195155
}
196156

197-
static Future<Response> patch(String url,
198-
{headers,
199-
bodyEncoding = DEFAULT_BODY_ENCODING,
200-
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
201-
persistCookies = true,
202-
verify = true}) {
203-
return _httpRequest(HTTP_METHOD_PATCH, url,
204-
bodyEncoding: bodyEncoding,
205-
headers: headers,
206-
timeoutSeconds: timeoutSeconds,
207-
persistCookies: persistCookies,
208-
verify: verify);
157+
static Future<Response> patch(String url, {headers, bodyEncoding = DEFAULT_BODY_ENCODING, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, persistCookies = true, verify = true}) {
158+
return _httpRequest(HTTP_METHOD_PATCH, url, bodyEncoding: bodyEncoding, headers: headers, timeoutSeconds: timeoutSeconds, persistCookies: persistCookies, verify: verify);
209159
}
210160

211-
static Future<Response> delete(String url,
212-
{headers,
213-
bodyEncoding = DEFAULT_BODY_ENCODING,
214-
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
215-
persistCookies = true,
216-
verify = true}) {
217-
return _httpRequest(HTTP_METHOD_DELETE, url,
218-
bodyEncoding: bodyEncoding,
219-
headers: headers,
220-
timeoutSeconds: timeoutSeconds,
221-
persistCookies: persistCookies,
222-
verify: verify);
161+
static Future<Response> delete(String url, {headers, bodyEncoding = DEFAULT_BODY_ENCODING, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, persistCookies = true, verify = true}) {
162+
return _httpRequest(HTTP_METHOD_DELETE, url, bodyEncoding: bodyEncoding, headers: headers, timeoutSeconds: timeoutSeconds, persistCookies: persistCookies, verify: verify);
223163
}
224164

225-
static Future<Response> post(String url,
226-
{json,
227-
body,
228-
bodyEncoding = DEFAULT_BODY_ENCODING,
229-
headers,
230-
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
231-
persistCookies = true,
232-
verify = true}) {
233-
return _httpRequest(HTTP_METHOD_POST, url,
234-
bodyEncoding: bodyEncoding,
235-
json: json,
236-
body: body,
237-
headers: headers,
238-
timeoutSeconds: timeoutSeconds,
239-
persistCookies: persistCookies,
240-
verify: verify);
165+
static Future<Response> post(String url, {json, body, bodyEncoding = DEFAULT_BODY_ENCODING, headers, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, persistCookies = true, verify = true}) {
166+
return _httpRequest(HTTP_METHOD_POST, url, bodyEncoding: bodyEncoding, json: json, body: body, headers: headers, timeoutSeconds: timeoutSeconds, persistCookies: persistCookies, verify: verify);
241167
}
242168

243-
static Future<Response> put(String url,
244-
{json,
245-
body,
246-
bodyEncoding = DEFAULT_BODY_ENCODING,
247-
headers,
248-
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
249-
persistCookies = true,
250-
verify = true}) {
251-
return _httpRequest(HTTP_METHOD_PUT, url,
252-
bodyEncoding: bodyEncoding,
253-
json: json,
254-
body: body,
255-
headers: headers,
256-
timeoutSeconds: timeoutSeconds,
257-
persistCookies: persistCookies,
258-
verify: verify);
169+
static Future<Response> put(String url, {json, body, bodyEncoding = DEFAULT_BODY_ENCODING, headers, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, persistCookies = true, verify = true}) {
170+
return _httpRequest(HTTP_METHOD_PUT, url, bodyEncoding: bodyEncoding, json: json, body: body, headers: headers, timeoutSeconds: timeoutSeconds, persistCookies: persistCookies, verify: verify);
259171
}
260172

261-
static Future<Response> _httpRequest(String method, String url,
262-
{json,
263-
body,
264-
bodyEncoding = DEFAULT_BODY_ENCODING,
265-
headers,
266-
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
267-
persistCookies = true,
268-
verify = true}) async {
173+
static Future<Response> _httpRequest(String method, String url, {json, body, bodyEncoding = DEFAULT_BODY_ENCODING, headers, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, persistCookies = true, verify = true}) async {
269174
http.Client client;
270175
if (!verify) {
271176
// Ignore SSL errors
@@ -280,13 +185,12 @@ class Requests {
280185
var uri = Uri.parse(url);
281186

282187
if (uri.scheme != 'http' && uri.scheme != 'https') {
283-
throw ArgumentError(
284-
"invalid url, must start with 'http://' or 'https://' sheme (e.g. 'http://example.com')");
188+
throw ArgumentError("invalid url, must start with 'http://' or 'https://' sheme (e.g. 'http://example.com')");
285189
}
286190

287191
String hostname = getHostname(url);
288192
headers = await _constructRequestHeaders(hostname, headers);
289-
dynamic bodyString = "";
193+
dynamic requestBody;
290194

291195
if (body != null && json != null) {
292196
throw ArgumentError('cannot use both "json" and "body" choose only one.');
@@ -299,22 +203,24 @@ class Requests {
299203

300204
if (body != null) {
301205
String contentTypeHeader;
302-
bodyString = body;
303-
if (body is String) {
206+
207+
if (bodyEncoding == RequestBodyEncoding.JSON) {
208+
requestBody = Common.toJson(body);
209+
contentTypeHeader = "application/json";
210+
}
211+
else if (bodyEncoding == RequestBodyEncoding.FormURLEncoded) {
212+
requestBody = Common.encodeMap(body);
213+
contentTypeHeader = "application/x-www-form-urlencoded";
214+
}
215+
else if (bodyEncoding == RequestBodyEncoding.PlainText) {
216+
requestBody = body;
304217
contentTypeHeader = "text/plain";
305-
} else if (body is Map || body is List) {
306-
if (bodyEncoding == RequestBodyEncoding.JSON) {
307-
bodyString = Common.toJson(body);
308-
contentTypeHeader = "application/json";
309-
} else if (bodyEncoding == RequestBodyEncoding.FormURLEncoded) {
310-
contentTypeHeader = "application/x-www-form-urlencoded";
311-
} else {
312-
throw Exception('unsupported bodyEncoding "$bodyEncoding"');
313-
}
218+
}
219+
else {
220+
throw Exception('unsupported bodyEncoding "$bodyEncoding"');
314221
}
315222

316-
if (contentTypeHeader != null &&
317-
!Common.hasKeyIgnoreCase(headers, "content-type")) {
223+
if (contentTypeHeader != null && !Common.hasKeyIgnoreCase(headers, "content-type")) {
318224
headers["content-type"] = contentTypeHeader;
319225
}
320226
}
@@ -326,19 +232,19 @@ class Requests {
326232
future = client.get(uri, headers: headers);
327233
break;
328234
case HTTP_METHOD_PUT:
329-
future = client.put(uri, body: bodyString, headers: headers);
235+
future = client.put(uri, body: requestBody, headers: headers);
330236
break;
331237
case HTTP_METHOD_DELETE:
332238
future = client.delete(uri, headers: headers);
333239
break;
334240
case HTTP_METHOD_POST:
335-
future = client.post(uri, body: bodyString, headers: headers);
241+
future = client.post(uri, body: requestBody, headers: headers);
336242
break;
337243
case HTTP_METHOD_HEAD:
338244
future = client.head(uri, headers: headers);
339245
break;
340246
case HTTP_METHOD_PATCH:
341-
future = client.patch(uri, body: bodyString, headers: headers);
247+
future = client.patch(uri, body: requestBody, headers: headers);
342248
break;
343249
default:
344250
throw Exception('unsupported http method "$method"');

logo/banner-01.png

-29.7 KB
Loading

logo/banner.ai

Lines changed: 4596 additions & 3434 deletions
Large diffs are not rendered by default.

test/requests_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void main() {
8282
test('remove cookies', () async {
8383
String url = "$PLACEHOLDER_PROVIDER/api/users/1";
8484
String hostname = Requests.getHostname(url);
85-
expect("reqres.in", hostname);
85+
expect("reqres.in:443", hostname);
8686
await Requests.clearStoredCookies(hostname);
8787
await Requests.setStoredCookies(hostname, {'session': 'bla'});
8888
var cookies = await Requests.getStoredCookies(hostname);

0 commit comments

Comments
 (0)