faraday_json 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.travis.yml +25 -0
- data/Gemfile +18 -0
- data/Gemfile-0.7.rb +3 -0
- data/Gemfile-0.7.rb.lock +57 -0
- data/Gemfile-0.8.rb +3 -0
- data/Gemfile-0.8.rb.lock +53 -0
- data/Gemfile.lock +52 -0
- data/LICENSE +30 -0
- data/README.md +30 -0
- data/Rakefile +28 -0
- data/faraday_json.gemspec +25 -0
- data/lib/faraday_json.rb +22 -0
- data/lib/faraday_json/encode_json.rb +94 -0
- data/lib/faraday_json/encoding.rb +219 -0
- data/lib/faraday_json/parse_json.rb +165 -0
- data/lib/faraday_json/version.rb +10 -0
- data/spec/data/iso8859-15_file.json +1 -0
- data/spec/data/utf16be_file.json +0 -0
- data/spec/data/utf16le_file.json +0 -0
- data/spec/data/utf8_file.json +1 -0
- data/spec/encode_json_spec.rb +376 -0
- data/spec/helper.rb +74 -0
- data/spec/parse_json_spec.rb +217 -0
- metadata +123 -0
@@ -0,0 +1,219 @@
|
|
1
|
+
#
|
2
|
+
# FaradayJSON
|
3
|
+
# https://github.com/spriteCloud/faraday_json
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 spriteCloud B.V. and other FaradayJSON contributors.
|
6
|
+
# All rights reserved.
|
7
|
+
#
|
8
|
+
|
9
|
+
module FaradayJSON
|
10
|
+
|
11
|
+
# Character encoding helper functions
|
12
|
+
module Encoding
|
13
|
+
|
14
|
+
# Two versions of transcode, one for Ruby 1.8 and one for greater versions.
|
15
|
+
|
16
|
+
if RUBY_VERSION.start_with?("1.8")
|
17
|
+
|
18
|
+
def transcode(data, input_charset, output_charset, opts = {})
|
19
|
+
# In Ruby 1.8, we pretty much have to believe the given charsets; there's
|
20
|
+
# not a lot of choice.
|
21
|
+
|
22
|
+
# If we don't have an input charset, we can't do better than US-ASCII.
|
23
|
+
if input_charset.nil? or input_charset.empty?
|
24
|
+
input_charset = opts.fetch('default_input_charset', 'us-ascii')
|
25
|
+
end
|
26
|
+
|
27
|
+
# The default output charset, on the other hand, should be UTF-8.
|
28
|
+
if output_charset.nil? or output_charset.empty?
|
29
|
+
output_charset = opts.fetch('default_output_charset', 'UTF-8//IGNORE')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Transcode using iconv
|
33
|
+
require 'iconv'
|
34
|
+
return ::Iconv.conv(output_charset, input_charset, data)
|
35
|
+
end
|
36
|
+
|
37
|
+
else # end ruby 1.8/start ruby > 1.8
|
38
|
+
|
39
|
+
def transcode(data, input_charset, output_charset, opts = {})
|
40
|
+
# Strings have an encode function in Ruby > 1.8
|
41
|
+
if not data.respond_to?(:encode)
|
42
|
+
return data
|
43
|
+
end
|
44
|
+
|
45
|
+
# If we don't have a charset, just use whatever is in the string
|
46
|
+
# currently. If we do have a charset, we'll have to run some extra
|
47
|
+
# checks.
|
48
|
+
if not (input_charset.nil? or input_charset.empty?)
|
49
|
+
# Check passed charset is *understood* by finding it. If this fails,
|
50
|
+
# an exception is raised, which it also should be.
|
51
|
+
canonical = ::Encoding.find(input_charset)
|
52
|
+
|
53
|
+
# Second, ensure the canonical charset and the actual string encoding
|
54
|
+
# are identical. If not, we'll have to do a little more than just
|
55
|
+
# transcode to UTF-8.
|
56
|
+
if canonical != data.encoding
|
57
|
+
if opts.fetch('force_input_charset', false)
|
58
|
+
data.force_encoding(canonical)
|
59
|
+
else
|
60
|
+
raise "Provided charset was #{canonical}, but data was #{data.encoding}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# If there's no output charset, we should default to UTF-8.
|
66
|
+
if output_charset.nil? or output_charset.empty?
|
67
|
+
output_charset = opts.fetch('default_output_charset', 'UTF-8')
|
68
|
+
end
|
69
|
+
|
70
|
+
# Transcode!
|
71
|
+
return data.encode(output_charset)
|
72
|
+
end
|
73
|
+
|
74
|
+
end # ruby > 1.8
|
75
|
+
|
76
|
+
# Convenient helper. Output is UTF-8. Input is either a string, or some data
|
77
|
+
# data. There's a Ruby 1.8 version mostly because it has to iteratively convert
|
78
|
+
# included strings.
|
79
|
+
if RUBY_VERSION.start_with?("1.8")
|
80
|
+
|
81
|
+
def to_utf8(data, charset, opts = {})
|
82
|
+
if data.is_a? Hash
|
83
|
+
transcoded = {}
|
84
|
+
data.each do |key, value|
|
85
|
+
transcoded[to_utf8(key, charset, opts)] = to_utf8(value, charset, opts)
|
86
|
+
end
|
87
|
+
return transcoded
|
88
|
+
elsif data.is_a? Array
|
89
|
+
transcoded = []
|
90
|
+
data.each do |value|
|
91
|
+
transcoded << to_utf8(value, charest, opts)
|
92
|
+
end
|
93
|
+
return transcoded
|
94
|
+
elsif data.is_a? String
|
95
|
+
return transcode(data, charset, 'UTF-8//IGNORE', opts)
|
96
|
+
else
|
97
|
+
return data
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
else # end ruby 1.8/start ruby > 1.8
|
102
|
+
|
103
|
+
def to_utf8(data, charset, opts = {})
|
104
|
+
return transcode(data, charset, 'UTF-8', opts)
|
105
|
+
end
|
106
|
+
|
107
|
+
end # ruby > 1.8
|
108
|
+
|
109
|
+
|
110
|
+
# Helper function; strips a BOM for UTF-16 encodings
|
111
|
+
def strip_bom(data, charset, opts = {})
|
112
|
+
# Only need to do this on Strings
|
113
|
+
if not data.is_a? String
|
114
|
+
return data
|
115
|
+
end
|
116
|
+
|
117
|
+
# If the charset is given, it overrides string internal encoding.
|
118
|
+
enc = get_dominant_encoding(data, charset, opts)
|
119
|
+
|
120
|
+
# Make the encoding canonical (if we can find out about that).
|
121
|
+
canonical = get_canonical_encoding(enc)
|
122
|
+
|
123
|
+
# Determine what a BOM would look like.
|
124
|
+
bom = get_bom(canonical)
|
125
|
+
|
126
|
+
# We can't operate on data, we need a byte array.
|
127
|
+
arr = data.each_byte.to_a
|
128
|
+
|
129
|
+
# Match BOM
|
130
|
+
found = true
|
131
|
+
bom.each_index do |i|
|
132
|
+
if bom[i] != arr[i]
|
133
|
+
found = false
|
134
|
+
break
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# So we may have found a BOM! Strip it.
|
139
|
+
if found
|
140
|
+
ret = arr[bom.length..-1].pack('c*')
|
141
|
+
if ret.respond_to? :force_encoding
|
142
|
+
ret.force_encoding(canonical)
|
143
|
+
end
|
144
|
+
return ret
|
145
|
+
end
|
146
|
+
|
147
|
+
# No BOM
|
148
|
+
return data
|
149
|
+
end
|
150
|
+
|
151
|
+
# Given a String with (potentially, this depends on Ruby version) an encoding,
|
152
|
+
# and a charset from a content-type header (which may be nil), determines the
|
153
|
+
# dominant encoding. (Charset, if given, overrides internal encoding,
|
154
|
+
# if present).
|
155
|
+
def get_dominant_encoding(str, charset, opts = {})
|
156
|
+
enc = nil
|
157
|
+
if str.respond_to? :encoding
|
158
|
+
enc = str.encoding
|
159
|
+
end
|
160
|
+
|
161
|
+
if charset.nil? or charset.empty?
|
162
|
+
if enc.nil?
|
163
|
+
default_encoding = opts.fetch('default_encoding', nil)
|
164
|
+
if default_encoding.nil?
|
165
|
+
raise "No charset provided, don't know what to do!" # FIXME
|
166
|
+
end
|
167
|
+
enc = default_encoding
|
168
|
+
end
|
169
|
+
else
|
170
|
+
enc = charset
|
171
|
+
end
|
172
|
+
|
173
|
+
return enc
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
# Returns a canonical version of an encoding.
|
178
|
+
def get_canonical_encoding(enc)
|
179
|
+
if defined? ::Encoding and ::Encoding.respond_to? :find
|
180
|
+
# Oh... Ruby 1.9.2 doesn't like passing an Encoding to find()...
|
181
|
+
if not enc.is_a? ::Encoding
|
182
|
+
enc = ::Encoding.find(enc)
|
183
|
+
end
|
184
|
+
return enc.to_s.downcase
|
185
|
+
end
|
186
|
+
return enc.downcase
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
# Given a (canonical) encoding, returns a BOM as an array of byte values. If
|
191
|
+
# the given encoding does not have a BOM, an empty array is returned.
|
192
|
+
def get_bom(enc)
|
193
|
+
bom = []
|
194
|
+
if enc.start_with?('utf16be') or enc.start_with?('utf-16be')
|
195
|
+
bom = [0xfe, 0xff]
|
196
|
+
elsif enc.start_with?('utf16le') or enc.start_with?('utf-16le')
|
197
|
+
bom = [0xff, 0xfe]
|
198
|
+
elsif enc.start_with?('utf8') or enc.start_with?('utf-8')
|
199
|
+
bom = [0xef, 0xbb, 0xbf]
|
200
|
+
elsif enc.start_with?('utf32be') or enc.start_with?('utf-32be')
|
201
|
+
bom = [0x00, 0x00, 0xfe, 0xff]
|
202
|
+
elsif enc.start_with?('utf32le') or enc.start_with?('utf-32le')
|
203
|
+
bom = [0xff, 0xfe, 0x00, 0x00]
|
204
|
+
end
|
205
|
+
return bom
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
|
210
|
+
# Helper function for testing
|
211
|
+
def bin_to_hex(data)
|
212
|
+
if data.respond_to? :each_byte
|
213
|
+
return data.each_byte.map { |b| b.to_s(16) }.join
|
214
|
+
end
|
215
|
+
return data
|
216
|
+
end
|
217
|
+
|
218
|
+
end # module Encoding
|
219
|
+
end # module FaradayJSON
|
@@ -0,0 +1,165 @@
|
|
1
|
+
#
|
2
|
+
# FaradayJSON
|
3
|
+
# https://github.com/spriteCloud/faraday_json
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 spriteCloud B.V. and other FaradayJSON contributors.
|
6
|
+
# All rights reserved.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'faraday_json/encoding'
|
10
|
+
|
11
|
+
module FaradayJSON
|
12
|
+
# Public: Parse response bodies as JSON.
|
13
|
+
class ParseJson < Faraday::Middleware
|
14
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
15
|
+
|
16
|
+
include ::FaradayJSON::Encoding
|
17
|
+
|
18
|
+
dependency do
|
19
|
+
require 'json' unless defined?(::JSON)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(app = nil, options = {})
|
23
|
+
super(app)
|
24
|
+
@options = options
|
25
|
+
@content_types = Array(options[:content_type])
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(environment)
|
29
|
+
@app.call(environment).on_complete do |env|
|
30
|
+
if process_response_type?(response_type(env)) and parse_response?(env)
|
31
|
+
process_response(env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_response(env)
|
37
|
+
env[:raw_body] = env[:body] if preserve_raw?(env)
|
38
|
+
body = env[:body]
|
39
|
+
|
40
|
+
# Body will be in an unknown encoding. Use charset field to coerce it to
|
41
|
+
# internal UTF-8.
|
42
|
+
charset = response_charset(env)
|
43
|
+
|
44
|
+
# We must ensure we're interpreting the body as the right charset. First,
|
45
|
+
# strip the BOM (if any).
|
46
|
+
body = strip_bom(body, charset, { 'default_encoding' => 'us-ascii' })
|
47
|
+
|
48
|
+
# Transcode to UTF-8
|
49
|
+
body = to_utf8(body, charset, { 'force_input_charset' => true })
|
50
|
+
|
51
|
+
# Now that's done, parse the JSON.
|
52
|
+
ret = nil
|
53
|
+
begin
|
54
|
+
ret = ::JSON.parse(body) unless body.strip.empty?
|
55
|
+
rescue StandardError, SyntaxError => err
|
56
|
+
raise err if err.is_a? SyntaxError and err.class.name != 'Psych::SyntaxError'
|
57
|
+
raise Faraday::Error::ParsingError, err
|
58
|
+
end
|
59
|
+
env[:body] = ret
|
60
|
+
end
|
61
|
+
|
62
|
+
def response_type(env)
|
63
|
+
type = env[:response_headers][CONTENT_TYPE].to_s
|
64
|
+
type = type.split(';', 2).first if type.index(';')
|
65
|
+
type
|
66
|
+
end
|
67
|
+
|
68
|
+
def response_charset(env)
|
69
|
+
header = env[:response_headers][CONTENT_TYPE].to_s
|
70
|
+
if header.index(';')
|
71
|
+
header.split(';').each do |part|
|
72
|
+
if part.index('charset=')
|
73
|
+
return part.split('charset=', 2).last
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
return nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_response_type?(type)
|
81
|
+
@content_types.empty? or @content_types.any? { |pattern|
|
82
|
+
pattern.is_a?(Regexp) ? type =~ pattern : type == pattern
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse_response?(env)
|
87
|
+
env[:body].respond_to? :to_str
|
88
|
+
end
|
89
|
+
|
90
|
+
def preserve_raw?(env)
|
91
|
+
env[:request].fetch(:preserve_raw, @options[:preserve_raw])
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
# DRAGONS
|
97
|
+
module OptionsExtension
|
98
|
+
attr_accessor :preserve_raw
|
99
|
+
|
100
|
+
def to_hash
|
101
|
+
super.update(:preserve_raw => preserve_raw)
|
102
|
+
end
|
103
|
+
|
104
|
+
def each
|
105
|
+
return to_enum(:each) unless block_given?
|
106
|
+
super
|
107
|
+
yield :preserve_raw, preserve_raw
|
108
|
+
end
|
109
|
+
|
110
|
+
def fetch(key, *args)
|
111
|
+
if :preserve_raw == key
|
112
|
+
value = __send__(key)
|
113
|
+
value.nil? ? args.fetch(0) : value
|
114
|
+
else
|
115
|
+
super
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
if defined?(Faraday::RequestOptions)
|
121
|
+
begin
|
122
|
+
Faraday::RequestOptions.from(:preserve_raw => true)
|
123
|
+
rescue NoMethodError
|
124
|
+
Faraday::RequestOptions.send(:include, OptionsExtension)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end # class ParseJson
|
128
|
+
|
129
|
+
# Public: Override the content-type of the response with "application/json"
|
130
|
+
# if the response body looks like it might be JSON, i.e. starts with an
|
131
|
+
# open bracket.
|
132
|
+
#
|
133
|
+
# This is to fix responses from certain API providers that insist on serving
|
134
|
+
# JSON with wrong MIME-types such as "text/javascript".
|
135
|
+
class ParseJsonMimeTypeFix < ParseJson
|
136
|
+
MIME_TYPE = 'application/json'.freeze
|
137
|
+
|
138
|
+
def process_response(env)
|
139
|
+
old_type = env[:response_headers][CONTENT_TYPE].to_s
|
140
|
+
new_type = MIME_TYPE.dup
|
141
|
+
new_type << ';' << old_type.split(';', 2).last if old_type.index(';')
|
142
|
+
env[:response_headers][CONTENT_TYPE] = new_type
|
143
|
+
end
|
144
|
+
|
145
|
+
BRACKETS = %w- [ { -
|
146
|
+
WHITESPACE = [ " ", "\n", "\r", "\t" ]
|
147
|
+
|
148
|
+
def parse_response?(env)
|
149
|
+
super and BRACKETS.include? first_char(env[:body])
|
150
|
+
end
|
151
|
+
|
152
|
+
def first_char(body)
|
153
|
+
idx = -1
|
154
|
+
begin
|
155
|
+
char = body[idx += 1]
|
156
|
+
char = char.chr if char
|
157
|
+
end while char and WHITESPACE.include? char
|
158
|
+
char
|
159
|
+
end
|
160
|
+
end # class ParseJson
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
# deprecated alias
|
165
|
+
Faraday::Response::ParseJson = FaradayJSON::ParseJson
|
@@ -0,0 +1 @@
|
|
1
|
+
{"a":"Hell�, W�rld!"}
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
{"a":"Hellö, Wörld!"}
|
@@ -0,0 +1,376 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
require 'faraday_json/encode_json'
|
5
|
+
|
6
|
+
describe FaradayJSON::EncodeJson do
|
7
|
+
let(:middleware) { described_class.new(lambda{|env| env}) }
|
8
|
+
|
9
|
+
def process(body, content_type = nil)
|
10
|
+
env = {:body => body, :request_headers => Faraday::Utils::Headers.new}
|
11
|
+
env[:request_headers]['content-type'] = content_type if content_type
|
12
|
+
middleware.call(faraday_env(env))
|
13
|
+
end
|
14
|
+
|
15
|
+
def result_body() result[:body] end
|
16
|
+
def result_type() result[:request_headers]['content-type'] end
|
17
|
+
def result_length() result[:request_headers]['content-length'].to_i end
|
18
|
+
|
19
|
+
context "no body" do
|
20
|
+
let(:result) { process(nil) }
|
21
|
+
|
22
|
+
it "doesn't change body" do
|
23
|
+
expect(result_body).to be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "doesn't add content type" do
|
27
|
+
expect(result_type).to be_nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "empty body" do
|
32
|
+
let(:result) { process('') }
|
33
|
+
|
34
|
+
it "doesn't change body" do
|
35
|
+
expect(result_body).to be_empty
|
36
|
+
end
|
37
|
+
|
38
|
+
it "doesn't add content type" do
|
39
|
+
expect(result_type).to be_nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "string body" do
|
44
|
+
let(:result) { process('{"a":1}') }
|
45
|
+
|
46
|
+
it "doesn't change body" do
|
47
|
+
expect(result_body).to eq('{"a":1}')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "adds content type" do
|
51
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "object body" do
|
56
|
+
let(:result) { process({:a => 1}) }
|
57
|
+
|
58
|
+
it "encodes body" do
|
59
|
+
expect(result_body).to eq('{"a":1}')
|
60
|
+
end
|
61
|
+
|
62
|
+
it "adds content type" do
|
63
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "empty object body" do
|
68
|
+
let(:result) { process({}) }
|
69
|
+
|
70
|
+
it "encodes body" do
|
71
|
+
expect(result_body).to eq('{}')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "object body with json type" do
|
76
|
+
let(:result) { process({:a => 1}, 'application/json; charset=utf-8') }
|
77
|
+
|
78
|
+
it "encodes body" do
|
79
|
+
expect(result_body).to eq('{"a":1}')
|
80
|
+
end
|
81
|
+
|
82
|
+
it "doesn't change content type" do
|
83
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "object body with incompatible type" do
|
88
|
+
let(:result) { process({:a => 1}, 'application/xml; charset=utf-8') }
|
89
|
+
|
90
|
+
it "doesn't change body" do
|
91
|
+
expect(result_body).to eq({:a => 1})
|
92
|
+
end
|
93
|
+
|
94
|
+
it "doesn't change content type" do
|
95
|
+
expect(result_type).to eq('application/xml; charset=utf-8')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
### Unicode test cases
|
100
|
+
# Ruby 1.8 will almost certainly fail if there is no charset given in a header.
|
101
|
+
# In Ruby >1.8, we have some more methods for guessing well.
|
102
|
+
|
103
|
+
### All Ruby versions should work with a charset given.
|
104
|
+
context "utf-8 in string body" do
|
105
|
+
let(:result) { process('{"a":"ä"}', 'application/json; charset=utf-8') }
|
106
|
+
|
107
|
+
it "doesn't change body" do
|
108
|
+
expect(result_body).to eq('{"a":"ä"}')
|
109
|
+
end
|
110
|
+
|
111
|
+
it "doesn't change content type" do
|
112
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
113
|
+
end
|
114
|
+
|
115
|
+
it "adds content length" do
|
116
|
+
expect(result_length).to eq(10)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "utf-8 in object body" do
|
121
|
+
let(:result) { process({:a => "ä"}, 'application/json; charset=utf-8') }
|
122
|
+
|
123
|
+
it "encodes body" do
|
124
|
+
expect(result_body).to eq('{"a":"ä"}')
|
125
|
+
end
|
126
|
+
|
127
|
+
it "doesn't change content type" do
|
128
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
129
|
+
end
|
130
|
+
|
131
|
+
it "adds content length" do
|
132
|
+
expect(result_length).to eq(10)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "non-unicode in string body" do
|
137
|
+
let(:result) {
|
138
|
+
process(test_encode('{"a":"ä"}', 'iso-8859-15'), 'application/json; charset=iso-8859-15')
|
139
|
+
}
|
140
|
+
|
141
|
+
it "changes body" do
|
142
|
+
expect(result_body).to eq('{"a":"ä"}')
|
143
|
+
end
|
144
|
+
|
145
|
+
it "changes content type" do
|
146
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
147
|
+
end
|
148
|
+
|
149
|
+
it "adds content length" do
|
150
|
+
expect(result_length).to eq(10)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "non-unicode in object body" do
|
155
|
+
let(:result) {
|
156
|
+
process({:a => test_encode('ä', 'iso-8859-15')}, 'application/json; charset=iso-8859-15')
|
157
|
+
}
|
158
|
+
|
159
|
+
it "encodes body" do
|
160
|
+
expect(result_body).to eq('{"a":"ä"}')
|
161
|
+
end
|
162
|
+
|
163
|
+
it "changes content type" do
|
164
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
165
|
+
end
|
166
|
+
|
167
|
+
it "adds content length" do
|
168
|
+
expect(result_length).to eq(10)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "non-utf-8 in string body" do
|
173
|
+
let(:result) {
|
174
|
+
process(test_encode('{"a":"ä"}', 'utf-16be'), 'application/json; charset=utf-16be')
|
175
|
+
}
|
176
|
+
|
177
|
+
it "changes body" do
|
178
|
+
expect(result_body).to eq('{"a":"ä"}')
|
179
|
+
end
|
180
|
+
|
181
|
+
it "changes content type" do
|
182
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
183
|
+
end
|
184
|
+
|
185
|
+
it "adds content length" do
|
186
|
+
expect(result_length).to eq(10)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "non-utf-8 in object body" do
|
191
|
+
let(:result) {
|
192
|
+
process({:a => test_encode('ä', 'utf-16le')}, 'application/json; charset=utf-16le')
|
193
|
+
}
|
194
|
+
|
195
|
+
it "encodes body" do
|
196
|
+
expect(result_body).to eq('{"a":"ä"}')
|
197
|
+
end
|
198
|
+
|
199
|
+
it "changes content type" do
|
200
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
201
|
+
end
|
202
|
+
|
203
|
+
it "adds content length" do
|
204
|
+
expect(result_length).to eq(10)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
### Ruby versions > 1.8 should be able to guess missing charsets at times.
|
210
|
+
if not RUBY_VERSION.start_with?("1.8")
|
211
|
+
context "utf-8 in string body without content type" do
|
212
|
+
let(:result) { process('{"a":"ä"}') }
|
213
|
+
|
214
|
+
it "doesn't change body" do
|
215
|
+
expect(result_body).to eq('{"a":"ä"}')
|
216
|
+
end
|
217
|
+
|
218
|
+
it "adds content type" do
|
219
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
220
|
+
end
|
221
|
+
|
222
|
+
it "adds content length" do
|
223
|
+
expect(result_length).to eq(10)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context "utf-8 in object body without content type" do
|
228
|
+
let(:result) { process({:a => "ä"}) }
|
229
|
+
|
230
|
+
it "encodes body" do
|
231
|
+
expect(result_body).to eq('{"a":"ä"}')
|
232
|
+
end
|
233
|
+
|
234
|
+
it "adds content type" do
|
235
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
236
|
+
end
|
237
|
+
|
238
|
+
it "adds content length" do
|
239
|
+
expect(result_length).to eq(10)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
context "non-unicode in string body without content type" do
|
244
|
+
let(:result) {
|
245
|
+
process(test_encode('{"a":"ä"}', 'iso-8859-15'))
|
246
|
+
}
|
247
|
+
|
248
|
+
it "doesn't change body" do
|
249
|
+
expect(result_body).to eq('{"a":"ä"}')
|
250
|
+
end
|
251
|
+
|
252
|
+
it "adds content type" do
|
253
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
254
|
+
end
|
255
|
+
|
256
|
+
it "adds content length" do
|
257
|
+
expect(result_length).to eq(10)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
context "non-unicode in object body without content type" do
|
262
|
+
let(:result) {
|
263
|
+
process({:a => test_encode('ä', 'iso-8859-15')})
|
264
|
+
}
|
265
|
+
|
266
|
+
it "encodes body" do
|
267
|
+
expect(result_body).to eq('{"a":"ä"}')
|
268
|
+
end
|
269
|
+
|
270
|
+
it "adds content type" do
|
271
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
272
|
+
end
|
273
|
+
|
274
|
+
it "adds content length" do
|
275
|
+
expect(result_length).to eq(10)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
context "non-utf-8 in string body without content type" do
|
280
|
+
let(:result) {
|
281
|
+
process(test_encode('{"a":"ä"}', 'utf-16be'))
|
282
|
+
}
|
283
|
+
|
284
|
+
|
285
|
+
it "doesn't change body" do
|
286
|
+
expect(result_body).to eq('{"a":"ä"}')
|
287
|
+
end
|
288
|
+
|
289
|
+
it "adds content type" do
|
290
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
291
|
+
end
|
292
|
+
|
293
|
+
it "adds content length" do
|
294
|
+
expect(result_length).to eq(10)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context "non-utf-8 in object body without content type" do
|
299
|
+
let(:result) {
|
300
|
+
process({:a => test_encode('ä', 'utf-16le')})
|
301
|
+
}
|
302
|
+
|
303
|
+
it "encodes body" do
|
304
|
+
expect(result_body).to eq('{"a":"ä"}')
|
305
|
+
end
|
306
|
+
|
307
|
+
it "adds content type" do
|
308
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
309
|
+
end
|
310
|
+
|
311
|
+
it "adds content length" do
|
312
|
+
expect(result_length).to eq(10)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
### Dealing with files in various encoding should ideally be easy
|
317
|
+
FILES = {
|
318
|
+
'spec/data/iso8859-15_file.json' => 'iso-8859-15',
|
319
|
+
'spec/data/utf16be_file.json' => 'utf-16be',
|
320
|
+
'spec/data/utf16le_file.json' => 'utf-16le',
|
321
|
+
'spec/data/utf8_file.json' => 'utf-8',
|
322
|
+
}
|
323
|
+
|
324
|
+
|
325
|
+
FILES.each do |fname, enc|
|
326
|
+
context "reading #{enc} encoded file '#{fname}'" do
|
327
|
+
# Read the string from file; read binary/with encoding. Ruby 1.8 will
|
328
|
+
# ignore this, but must still work.
|
329
|
+
data = File.new(fname, "rb:#{enc}").read
|
330
|
+
|
331
|
+
# Passing that data with a charset should do the right thing.
|
332
|
+
let(:result) {
|
333
|
+
process(data)
|
334
|
+
}
|
335
|
+
|
336
|
+
it "encodes body" do
|
337
|
+
expect(result_body).to eq("{\"a\":\"Hellö, Wörld!\"}\n")
|
338
|
+
end
|
339
|
+
|
340
|
+
it "adds content type" do
|
341
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
342
|
+
end
|
343
|
+
|
344
|
+
it "adds content length" do
|
345
|
+
expect(result_length).to eq(24)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
FILES.each do |fname, enc|
|
351
|
+
context "reading #{enc} encoded file '#{fname}' as binary" do
|
352
|
+
# Read the string from file; read binary/with encoding. Ruby 1.8 will
|
353
|
+
# ignore this, but must still work.
|
354
|
+
data = File.new(fname, "rb").read
|
355
|
+
|
356
|
+
# Passing that data with a charset should do the right thing.
|
357
|
+
let(:result) {
|
358
|
+
process(data, "application/json; charset=#{enc}")
|
359
|
+
}
|
360
|
+
|
361
|
+
it "encodes body" do
|
362
|
+
expect(result_body).to eq("{\"a\":\"Hellö, Wörld!\"}\n")
|
363
|
+
end
|
364
|
+
|
365
|
+
it "adds content type" do
|
366
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
367
|
+
end
|
368
|
+
|
369
|
+
it "adds content length" do
|
370
|
+
expect(result_length).to eq(24)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
end
|
376
|
+
end
|