@@ -448,7 +448,7 @@ def initialize(src, &blk)
448
448
else
449
449
src
450
450
end
451
- @buf = String . new . b
451
+ @buf = String . new
452
452
@blk = blk
453
453
end
454
454
end
@@ -460,7 +460,7 @@ class << self
460
460
# @return [Enumerable<String>]
461
461
def writable_enum ( &blk )
462
462
Enumerator . new do |y |
463
- buf = String . new . b
463
+ buf = String . new
464
464
y . define_singleton_method ( :write ) do
465
465
self << buf . replace ( _1 )
466
466
buf . bytesize
@@ -582,14 +582,35 @@ def encode_content(headers, body)
582
582
583
583
# @api private
584
584
#
585
+ # https://www.iana.org/assignments/character-sets/character-sets.xhtml
586
+ #
587
+ # @param content_type [String]
588
+ # @param text [String]
589
+ def force_charset! ( content_type , text :)
590
+ charset = /charset=([^;\s ]+)/ . match ( content_type ) &.captures &.first
591
+
592
+ return unless charset
593
+
594
+ begin
595
+ encoding = Encoding . find ( charset )
596
+ text . force_encoding ( encoding )
597
+ rescue ArgumentError
598
+ nil
599
+ end
600
+ end
601
+
602
+ # @api private
603
+ #
604
+ # Assumes each chunk in stream has `Encoding::BINARY`.
605
+ #
585
606
# @param headers [Hash{String=>String}, Net::HTTPHeader]
586
607
# @param stream [Enumerable<String>]
587
608
# @param suppress_error [Boolean]
588
609
#
589
610
# @raise [JSON::ParserError]
590
611
# @return [Object]
591
612
def decode_content ( headers , stream :, suppress_error : false )
592
- case headers [ "content-type" ]
613
+ case ( content_type = headers [ "content-type" ] )
593
614
in %r{^application/(?:vnd\. api\+ )?json}
594
615
json = stream . to_a . join
595
616
begin
@@ -606,11 +627,10 @@ def decode_content(headers, stream:, suppress_error: false)
606
627
in %r{^text/event-stream}
607
628
lines = decode_lines ( stream )
608
629
decode_sse ( lines )
609
- in %r{^text/}
610
- stream . to_a . join
611
630
else
612
- # TODO: parsing other response types
613
- StringIO . new ( stream . to_a . join )
631
+ text = stream . to_a . join
632
+ force_charset! ( content_type , text : text )
633
+ StringIO . new ( text )
614
634
end
615
635
end
616
636
end
@@ -675,12 +695,17 @@ def chain_fused(enum, &blk)
675
695
class << self
676
696
# @api private
677
697
#
698
+ # Assumes Strings have been forced into having `Encoding::BINARY`.
699
+ #
700
+ # This decoder is responsible for reassembling lines split across multiple
701
+ # fragments.
702
+ #
678
703
# @param enum [Enumerable<String>]
679
704
#
680
705
# @return [Enumerable<String>]
681
706
def decode_lines ( enum )
682
707
re = /(\r \n |\r |\n )/
683
- buffer = String . new . b
708
+ buffer = String . new
684
709
cr_seen = nil
685
710
686
711
chain_fused ( enum ) do |y |
@@ -711,6 +736,8 @@ def decode_lines(enum)
711
736
#
712
737
# https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream
713
738
#
739
+ # Assumes that `lines` has been decoded with `#decode_lines`.
740
+ #
714
741
# @param lines [Enumerable<String>]
715
742
#
716
743
# @return [Enumerable<Hash{Symbol=>Object}>]
@@ -734,7 +761,7 @@ def decode_sse(lines)
734
761
in "event"
735
762
current . merge! ( event : value )
736
763
in "data"
737
- ( current [ :data ] ||= String . new . b ) << ( value << "\n " )
764
+ ( current [ :data ] ||= String . new ) << ( value << "\n " )
738
765
in "id" unless value . include? ( "\0 " )
739
766
current . merge! ( id : value )
740
767
in "retry" if /^\d +$/ =~ value
0 commit comments