やるきなし

2016/02/05 13:55 / WordPress RSS Aggregator がうまく Feed を受け取ってくれない (Not well-formed (invalid token) at line 1, column 1)

Feedを受け取る側で以下のようなエラーが出てFeedできないという話(受け取る側は業者で,こちらはFeedする側).

Failed to fetch the RSS feed. Error: This XML document is invalid,
likely due to invalid characters. XML error: Not well-formed
(invalid token) at line 1, column 1

Feed XML は Validator をとおっていて,かつ手元の simplepie で XML をパースしてみても問題がない.

そもそも line 1, column 1 でコケているので,受け取る側の問題なのだが,対応してくれないので,こちらで対応することにした.

原因はサーバ側が deflate (gzip) されたデータを正しくハンドリングできないこと.つまり,

[S->C]
GET /?feed=rss2 HTTP/1.1
Host: client.example.org
User-Agent: SimplePie/1.3.1 (Feed Parser; http://simplepie.org; Allow like Gecko) Build/2015XXXXXXXXXX
Accept-Encoding: x-gzip,gzip,deflate
Accept: application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1
Connection: Close

のリクエストに対して

[C->S]
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2016 XX:XX:XX GMT
Server: Apache
Set-Cookie: qtrans_front_language=ja; expires=Sat, 04-Feb-2017 XX:XX:XX GMT; Max-Age=31536000; path=/
Last-Modified: Mon, 01 Feb 2016 XX:XX:XX GMT
ETag: (snip)
Link: <http://client.example.org/?rest_route=/>; rel="https://api.w.org/"
Content-Length: 8000
Connection: close
Content-Type: application/rss+xml; charset=UTF-8

XMLデータ

のように Accept-Encoding: x-gzip,gzip,deflate を無視して RAW データ(といってもテキスト)を返す場合は OK だが,

[C->S]
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2016 XX:XX:XX GMT
Server: Apache
Set-Cookie: qtrans_front_language=ja; expires=Sat, 04-Feb-2017 XX:XX:XX GMT; Max-Age=31536000; path=/
Last-Modified: Mon, 01 Feb 2016 XX:XX:XX GMT
ETag: (snip)
Link: <http://client.example.org/?rest_route=/>; rel="https://api.w.org/"
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 2000
Connection: close
Content-Type: application/rss+xml; charset=UTF-8

圧縮されたXMLデータ

のように律儀に(というか apache のデフォルト) Content-Encoding: gzip で返すとコケる.クライアントというか Feed する側にて,この特定のサーバに対して,以下のように圧縮しないように設定すれば解決./?feed=rss2 が Feed URL なので,以下のように RewriteCond/RewriteRule で対処(RewriteEngine Onとかはこの上付近にあることを想定).

RewriteCond %{QUERY_STRING} feed=rss2
RewriteCond %{REMOTE_HOST} XXX.XXX.XXX.XXX
RewriteRule ^ - [E=no-gzip]

先方のサーバの設定が不明だが,Google で検索してもこの問題(の上記のような解決策)は出てこないようなので,かなりのコーナーケースな模様.

追記(2016/04/19)

WordPress の SimplePie の(サーバ側)該当箇所は wp-includes/SimplePie/File.php の以下の付近,だろうと思われる.

if (extension_loaded('zlib'))
{
         $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
}
// snip
case 'gzip':
case 'x-gzip':
        $decoder = new SimplePie_gzdecode($this->body);
        if (!$decoder->parse())
        {
                $this->error = 'Unable to decode HTTP "gzip" stream';
                $this->success = false;
        }
        else
        {
                $this->body = $decoder->data;
        }
        break;
// snip

$decoder->parse()前後でコケているのだと思われるが (上流のエラーハンドリングまでは今回追わず),上のような問題が発生するような状況では if (extension_loaded('zlib')) の if 文まるごと削除してしまってもよいのかもしれない.