fusic/maintenance

MaintenanceMiddleware doesn't send 503

Opened this issue · 1 comments

はじめまして。 CakePHP コアチームの栗田です。

表題だけ英語にしてみましたが、こちら、日本語で問題を報告してもよろしかったでしょうか。
dereuromark さんより不具合の報告がありましたので、代わって私からお伝えさせてください。

表題の通り MaintenanceMiddleware が HTTP 503 を返していないようです。原因となっているのは以下のコードのようです。

$body = $response->withHeader('Content-Type', $contentType)
->withStatus($statusCode)
->getBody();

この処理で、 withHeader()withStatus() が返すイミュータブルなレスポンスを $response に格納し直さないまま 77 行目で返却しているため、ステータスコードを設定する前の古いレスポンスが返却されることになり、この問題を生じているとのことでした。

ただ、私は少々このふるまいに疑問を持ちまして Maintenance プラグインをインストールしてみました。
というのも、 75 行目で書き込んだボディが返されているのかどうかが気になったからです。もしも返されていないのであれば、メンテナンスページ自体が表示されないことになり、さすがに開発中に気付く気がしましたので。

$body->write($bodyString);

案の定、メンテナンスページ自体は正しく表示されました。それでふるまいを理解したのですが Zend\Diactoros\Response::getBody() はイミュータブルなオブジェクトを謳っているにも関わらず、オブジェクト内に内包しているミュータブルなハンドラを返してしまう実装のようです。
https://github.com/zendframework/zend-diactoros/blob/215f47a73fb2a7f2d33230a3e23658acae234035/src/MessageTrait.php#L294

そして、このミュータブルなハンドラは、新たなイミュータブルオブジェクトを返す際に配慮がなされることもなく、複製前と複製後で同じオブジェクトを参照します。したがって、新しいレスポンスのボディ対して行った書き込みが古いレスポンスのボディにも適用されることなり、結果的にメンテナンスページの HTML だけは正しく表示される、という状況になってしまっているみたいですね。

私はこの Zend\Diactoros\Response 実装は正しくない気がしており、他の開発者の意見も伺った上で必要であれば不具合として報告してみようと思います。とはいえ、現状はそのようにふるまうようですから、新しいレスポンスを使用する際には注意した方がよさそうですね。

以上、ご報告でした。

k1LoW commented

完全に見落としていました。。。