dart-lang / http

A composable API for making HTTP requests in Dart.
https://pub.dev/packages/http
BSD 3-Clause "New" or "Revised" License
1.03k stars 355 forks source link

Can't use custom encoding #252

Open rostopira opened 5 years ago

rostopira commented 5 years ago

I need to send post request with cp1251 encoding I've extended Encoding class with following code

import 'dart:convert';

class Cp1251 extends Encoding {

  @override
  Converter<List<int>, String> get decoder => Cp1251Decoder();

  @override
  Converter<String, List<int>> get encoder => Cp1251Encoder();

  @override
  String get name => "windows-1251";

}

class Cp1251Encoder extends Converter<String, List<int>> {

  @override
  List<int> convert(String input) {
    final result = List<int>();
    for (var i = 0; i < input.length; i++) {
      final code = input.codeUnitAt(i);
      if (code < 0x80)
        result.add(code);
      else {
        final cpCode = _MAP[code];
        if (cpCode != null)
          result.add(code);
      }
    }
    return result;
  }

}

class Cp1251Decoder extends Converter<List<int>, String> {

  @override
  String convert(List<int> input) {
    var result = "";
    final mapKeys = _MAP.keys.toList(growable: false);
    for (var i = 0; i < input.length; i++) {
      var code = input[i];
      if (code < 0x80)
        result += String.fromCharCode(code);
      else if (code <= 0xff) {
        code -= 0x80;
        result += String.fromCharCode(mapKeys[code]);
      }
    }
    return result;
  }

}

_MAP is just chars map for this encoding But using it throws an error

Unhandled exception:
FormatException: Unsupported encoding "windows-1251".
#0      requiredEncodingForCharset (package:http/src/utils.dart:56:3)
#1      Request.encoding (package:http/src/request.dart:51:12)
#2      Request.bodyFields= (package:http/src/request.dart:128:46)
#3      BaseClient._sendUnstreamed (package:http/src/base_client.dart:165:17)
<asynchronous suspension>
#4      BaseClient.post (package:http/src/base_client.dart:56:5)
#5      post.<anonymous closure> (package:http/http.dart:70:34)
#6      _withClient (package:http/http.dart:167:20)
<asynchronous suspension>
#7      post (package:http/http.dart:70:3)
#8      main (file:///Users/this/IdeaProjects/untitled3/src/main.dart:47:23)
<asynchronous suspension>
#9      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:300:19)
#10     _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

I've tried to replace encoding name with "ascii", but it throws as well

Unhandled exception:
Invalid argument (string): Contains invalid characters.: "ОАЭ  от 490$"
#0      _UnicodeSubsetEncoder.convert (dart:convert/ascii.dart:86:9)
#1      AsciiCodec.encode (dart:convert/ascii.dart:41:46)
#2      _Uri._uriEncode (dart:core/runtime/liburi_patch.dart:62:26)
#3      Uri.encodeQueryComponent (dart:core/uri.dart:1098:17)
#4      mapToQuery.<anonymous closure> (package:http/src/utils.dart:19:22)
#5      CastMap.forEach.<anonymous closure> (dart:_internal/cast.dart:286:8)
#6      __InternalLinkedHashMap&_HashVMBase&MapMixin&_LinkedHashMapMixin.forEach (dart:collection/runtime/libcompact_hash.dart:367:8)
#7      CastMap.forEach (dart:_internal/cast.dart:285:13)

How can I use non-default encoding?

tshedor commented 3 years ago

@rostopira our team recently encountered an almost identical problem, except we were trying to do GZip. We even went down the same path. I also tried fixing _defaultEncoding so that when encoding was set that encoding would be acknowledged, but this threw handshake errors.

However, the solution was not recycling the original request (the mutable members are misleading) but instead recreating the request and then adding the encoded value to bodyFields:

if (request is! http.Request) return innerClient.send(request);

final httpRequest = request as http.Request;
if (httpRequest.body == null || httpRequest.body.isEmpty) return innerClient.send(request);

var newRequest = http.Request(httpRequest.method, httpRequest.url);
newRequest.headers.addAll(httpRequest.headers);
newRequest.bodyBytes = _encoder.encode(httpRequest.bodyBytes);
newRequest.headers['Content-Encoding'] = 'gzip';

return innerClient.send(newRequest);

I discovered this after reviewing the source for _sendUnstreamed. The full pull request is here if you want to look at the complete code.

cc @Pholey @DenchikBY @anisalibegic from your reactions