blob: fd426a8b3f0423142c02289fd9ac0a86c0e21a9b [file] [log] [blame]
// 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.
@TestOn('vm')
library;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http_io;
import 'package:test/test.dart';
import '../utils.dart';
class TestClient extends http.BaseClient {
@override
Future<http.StreamedResponse> send(http.BaseRequest request) {
throw UnimplementedError();
}
}
class TestClient2 extends http.BaseClient {
@override
Future<http.StreamedResponse> send(http.BaseRequest request) {
throw UnimplementedError();
}
}
void main() {
late Uri serverUrl;
setUpAll(() async {
serverUrl = await startServer();
});
test('#send a StreamedRequest', () async {
var client = http.Client();
var request = http.StreamedRequest('POST', serverUrl)
..headers[HttpHeaders.contentTypeHeader] =
'application/json; charset=utf-8'
..headers[HttpHeaders.userAgentHeader] = 'Dart';
var responseFuture = client.send(request);
request.sink.add('{"hello": "world"}'.codeUnits);
unawaited(request.sink.close());
var response = await responseFuture;
expect(response.request, equals(request));
expect(response.statusCode, equals(200));
expect(response.headers['single'], equals('value'));
// dart:io internally normalizes outgoing headers so that they never
// have multiple headers with the same name, so there's no way to test
// whether we handle that case correctly.
var bytesString = await response.stream.bytesToString();
client.close();
expect(
bytesString,
parse(equals({
'method': 'POST',
'path': '/',
'headers': {
'content-type': ['application/json; charset=utf-8'],
'accept-encoding': ['gzip'],
'user-agent': ['Dart'],
'transfer-encoding': ['chunked']
},
'body': '{"hello": "world"}'
})));
});
test('#send a StreamedRequest with a custom client', () async {
var ioClient = HttpClient();
var client = http_io.IOClient(ioClient);
var request = http.StreamedRequest('POST', serverUrl)
..headers[HttpHeaders.contentTypeHeader] =
'application/json; charset=utf-8'
..headers[HttpHeaders.userAgentHeader] = 'Dart';
var responseFuture = client.send(request);
request.sink.add('{"hello": "world"}'.codeUnits);
unawaited(request.sink.close());
var response = await responseFuture;
expect(response.request, equals(request));
expect(response.statusCode, equals(200));
expect(response.headers['single'], equals('value'));
// dart:io internally normalizes outgoing headers so that they never
// have multiple headers with the same name, so there's no way to test
// whether we handle that case correctly.
var bytesString = await response.stream.bytesToString();
client.close();
expect(
bytesString,
parse(equals({
'method': 'POST',
'path': '/',
'headers': {
'content-type': ['application/json; charset=utf-8'],
'accept-encoding': ['gzip'],
'user-agent': ['Dart'],
'transfer-encoding': ['chunked']
},
'body': '{"hello": "world"}'
})));
});
test('#send with an invalid URL', () {
var client = http.Client();
var url = Uri.http('http.invalid', '');
var request = http.StreamedRequest('POST', url);
request.headers[HttpHeaders.contentTypeHeader] =
'application/json; charset=utf-8';
expect(
client.send(request),
throwsA(allOf(
isA<http.ClientException>().having((e) => e.uri, 'uri', url),
isA<SocketException>().having(
(e) => e.toString(),
'SocketException.toString',
matches('ClientException with SocketException.*,'
' uri=http://http.invalid')))));
request.sink.add('{"hello": "world"}'.codeUnits);
request.sink.close();
});
test('sends a MultipartRequest with correct content-type header', () async {
var client = http.Client();
var request = http.MultipartRequest('POST', serverUrl);
var response = await client.send(request);
var bytesString = await response.stream.bytesToString();
client.close();
var headers = (jsonDecode(bytesString) as Map<String, dynamic>)['headers']
as Map<String, dynamic>;
var contentType = (headers['content-type'] as List).single;
expect(contentType, startsWith('multipart/form-data; boundary='));
});
test('detachSocket returns a socket from an IOStreamedResponse', () async {
var ioClient = HttpClient();
var client = http_io.IOClient(ioClient);
var request = http.Request('GET', serverUrl);
var response = await client.send(request);
var socket = await response.detachSocket();
expect(socket, isNotNull);
});
test('runWithClient', () {
final client = http.runWithClient(http.Client.new, TestClient.new);
expect(client, isA<TestClient>());
});
test('runWithClient Client() return', () {
final client = http.runWithClient(http.Client.new, http.Client.new);
expect(client, isA<http_io.IOClient>());
});
test('runWithClient nested', () {
late final http.Client client;
late final http.Client nestedClient;
http.runWithClient(() {
http.runWithClient(() => nestedClient = http.Client(), TestClient2.new);
client = http.Client();
}, TestClient.new);
expect(client, isA<TestClient>());
expect(nestedClient, isA<TestClient2>());
});
test('runWithClient recursion', () {
// Verify that calling the http.Client() factory inside nested Zones does
// not provoke an infinite recursion.
http.runWithClient(() {
http.runWithClient(http.Client.new, http.Client.new);
}, http.Client.new);
});
}