id:otn:20140901 の「MS Exchange/Outlook のカレンダーとGoogle Calendarの同期 」の続き。
ふと、Rubyをデバッグモードで実行してみると、
Exception `ArgumentError' at D:/Ruby/lib/ruby/gems/2.0.0/gems/multi_json-1.10.1/lib/multi_json/adapter.rb:30 - invalid byte sequence in Windows-31J
のエラーが出ている。
def blank?(input) input.nil? || /\A\s*\z/ === input rescue ArgumentError # invalid byte sequence in UTF-8 false end
と、レスキューされているので実際にはエラーにはならないし、意味上も問題ないが、気になる。これは、inputのEncodingがWindows-31Jになっているが実際にはUTF-8の値が入っていると言うことだろう。JSON周りと言うことは、Google API周辺で出ているはずで、実際プログラムを削ってみると、Google APIでリスト取得する段階でエラーになっていることがわかる。
Encoding.default_external = Encoding::UTF_8
を入れてみると、このエラーは出ない。ということは、Webから受け取ったデータのEncodingがデフォルトのままなので、Windows環境ではWindows-31Jと見なされてしまうというのが原因。default_external を変更できないケースもあるだろうから、ライブラリ側で何とかして欲しいところ。
ソースを見てみると、faradayライブラリが原因のよう。
require "faraday" a = Faraday.new u = %w| http://www.google.com/ http://www.google.com/search?q=AAAAA http://www.google.co.jp/ | u.each do |x| w = a.get(x) puts "#{w.status} #{w.body.encoding} #{w.headers['Content-Type']} #{x}" end ==> 302 UTF-8 text/html; charset=UTF-8 http://www.google.com/ ==> 200 ASCII-8BIT text/html; charset=ISO-8859-1 http://www.google.com/search?q=AAAAA ==> 200 ASCII-8BIT text/html; charset=Shift_JIS http://www.google.co.jp/
となる。他のURLも試してみると、200 だと ASCII-8BIT で、30x だと UTF-8 になるようだ。
charsetは見てない。faraday のソースを grep してもcharsetを気にしているらしい記述はない。
それではと思って、net/http を直接使って同様のプログラムを書いてみると、encoding は同様になる。
ということは、faraday は encoding については素通しで、そもそも、net/http で charset を見ずに ASCII-8BIT にしている。
しかし、これがどうして google-api-client を通すと Windows-31J になるのか?? default_external を変えると変わるし。
google-api-client のソースを見てもそれらしい記述はない。
とりあえずパッチを当てておく。
--- api_client.rb.bak 2014-08-11 23:35:44 +0900 +++ api_client.rb 2014-09-24 22:46:13 +0900 @@ -599,6 +599,7 @@ case result.status when 200...300 + result.body.force_encoding(Encoding::UTF_8) result when 301, 302, 303, 307 request = generate_request(request.to_hash.merge({
2014-09-25追記
原因判明。
レスポンスが "Content-Encoding: gzip" の時に、zlib を使って伸張しているが、その際に default_external になってしまっているようだ。
伸張する前の encodig は ASCII-8BIT だ。
ということで、パッチを当てるなら、api_client/gzip.rb に当てるべき。
https://github.com/google/google-api-ruby-client/issues/160#issuecomment-67225979