YouTubeにAPIでアップロードするとBroken pipeで接続を切られる問題
カテゴリ: Ruby, トラブルシューティング
YouTube Data APIの直接アップロードAPIを使用して動画のアップロード機能を実装していた時の話。ちなみに言語はRubyで認証にはRuby OAuthを使用している。それまではちゃんとアップロードで来ていたのに、いつの日からかYouTubeにリクエストを送ったところで以下のエラーが発生して失敗するようになってしまった。
Errno::EPIPE: Broken pipe /usr/local/lib/ruby/1.8/net/protocol.rb:175:in `write' /usr/local/lib/ruby/1.8/net/protocol.rb:175:in `write0' /usr/local/lib/ruby/1.8/net/protocol.rb:151:in `write' /usr/local/lib/ruby/1.8/net/protocol.rb:166:in `writing' /usr/local/lib/ruby/1.8/net/protocol.rb:150:in `write' ...
なんだか分からないがYouTube側から接続が切られているらしい。色々変なものを上げ続けたためにBANされたのかと思ったが、別のホストから行ってもBroken pipeになるので、どうやら送りつけるリクエストの問題らしい。原因のヒントが無いに等しい状態なので、色々試行錯誤を行ったところ、なんとか原因を探り当てることができた。Broken pipeになる原因はこれだ。
「リクエストの形式が正しくない場合に、リクエストのサイズが2MBを超えるとYouTube側から接続を切られる」
2MB(正確には2.15MBくらい)よりも小さな動画ファイルを使用してアップロードを行うと、ステータスコードが「400 Bad Request」、ボディが「Incomplete multipart body.」や「No file found in upload request.」となっているレスポンスが返ってくるようになった。おそらく「不正なリクエストに長々と付き合ってられん」ということで、ある程度の長さになると問答無用で切断するようになっているのだろう。こういうヒントも何も出ない挙動はドキュメントに記載してほしい。
さて、肝心のYouTubeにリクエストの形式が正しくないとされてしまう原因だが、エラーメッセージからするとマルチパートのバウンダリがちゃんと認識されていないようだ。アップロードAPIへのリクエストはメタ情報のXMLと動画ファイルの2つのパートから構成されており、これを当初は以下のように生成していた。
body = ""
body.concat("--#{boundary_string}\r\n")
body.concat("Content-Type: application/atom+xml; charset=UTF-8\r\n")
body.concat("\r\n")
body.concat <<EOS
<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007">
<media:group>
<media:title type="plain">#{title}</media:title>
<media:description type="plain">#{description}</media:description>
<media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">People</media:category>
<media:keywords>Foo</media:keywords>
</media:group>
</entry>
EOS
body.concat("--#{boundary_string}\r\n")
body.concat("Content-Type: #{content_type}\r\n")
...
ソースコードの改行コードはLFなので、2個目のバウンダリは「[LF]--boundary_string[CR][LF]」のようになる。これは「[CR][LF]--boundary_string[CR][LF]」のようになるのが正しいはずだ。ということでバウンダリの指定をbody.concat("\r\n--#{boundary_string}\r\n")にしたところアップロードできるようになった。おそらくうまく行っていた頃のYouTubeはLFだけの場合も許容してくれていたのだろう。それがいつかのタイミングで許容されなくなったと。こういう変更の情報がどこかに掲載されているとありがたいのだが、どこかにあったりするのだろうか。