http packageを使っている。
pub.dev
まあ、便利なんだけどtimeoutとcookieが対応していない。またUserAgentは常に指定する必要がある。
なので、それらに対応するには別packageを使うか自分で頑張る他ない。
諸々対応しているHTTP Clientとしてdioがあるが、自前で頑張っている+中国語でドキュメントが書かれていたりするので、今回は自分で頑張ることにした。
import 'dart:io';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:http/http.dart' as http;
class HttpClient extends http.BaseClient {
static const userAgent =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
final http.Client _inner = http.Client();
final CookieJar _cookieJar = CookieJar();
final Duration _timeout = Duration(seconds: 10);
@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
print("${request.method} ${request.url}");
request.headers[HttpHeaders.userAgentHeader] = userAgent;
final cookies = _cookieJar.loadForRequest(request.url);
_removeExpiredCookies(cookies);
String cookie = _getCookies(cookies);
if (cookie.isNotEmpty) {
request.headers[HttpHeaders.cookieHeader] = cookie;
}
final response = await _inner.send(request).timeout(_timeout);
if (response != null && response.headers != null) {
final cookieHeader = response.headers[HttpHeaders.setCookieHeader];
_saveCookies(response.request.url, cookieHeader);
}
return response;
}
void _removeExpiredCookies(List<Cookie> cookies) {
cookies.removeWhere((cookie) {
if (cookie.expires != null) {
return cookie.expires.isBefore(DateTime.now());
}
return false;
});
}
String _getCookies(List<Cookie> cookies) {
return cookies.map((cookie) => "${cookie.name}=${cookie.value}").join('; ');
}
void _saveCookies(Uri uri, String cookieHeader) {
if (cookieHeader == null || cookieHeader.isEmpty) {
return;
}
final cookies = cookieHeader.split(",");
if (cookies.isEmpty) {
return;
}
_cookieJar.saveFromResponse(
uri,
cookies.map((cookie) => Cookie.fromSetCookieValue(cookie)).toList(),
);
}
}
という感じでやっている。
また何度もリクエストするようなコードを書いていると Connection closed before full header was received というエラーが出ることがあり、それは1秒ほどdelayすると起きない。
final response1 = await http.get(...);
...
await Future<void>.delayed(Duration(seconds: 1));
...
final response2 = await http.get(...);