解決に1日要しました。涙
お役にたてば幸いです。
背景
- あるAPIを実装
- APIは簡易HTTPサーバ上で動作(HTTPメソッド=POSTで待受)
- Apacheをリバースプロキシとして利用
- 動作試験(curlや簡易クライアント)は良好
- 本番クライアント(.NETアプリ)からのリクエストでエラー(100%)
エラー内容
- Apacheが返しているHTTPステータスコードは503
- IP制限の場合は403エラーになるのでIP制限は無関係
- APIクライアントではタイムアウトエラー
- Apacheのエラーログの内容は以下
[proxy_http:error] [pid xxx] (104)Connection reset by peer: [client IPアドレス:ポート] \
AH01102: error reading status line from remote server バックエンドのIPアドレス:ポート
よくわからなかったので、Apacheのdumpioモジュールを有効にして、リクエストヘッダー等を記録。
試験と本番のリクエストの違いを調査してみたところ以下がわかりました。
本番(=エラー)リクエストにはHTTPヘッダーにExpect: 100-continue
が含まれている。(そしてbodyは空)
このHTTPヘッダを動作試験用のスクリプト(curl)に追加してみてたら同じ事象が再現しました。
これが原因のようです。
curl -H 'Expect: 100-continue' 以下省略
原因
Expect: 100-continue
ヘッダーは、「これからPOSTします(コンテンツ長はこれです)けどいいですか?」というサーバへの問い合わせです。
サーバはリクエストを受け取れる場合は「いいですよ。」とHTTPステータスコード100
を返す必要があります。
ところが、バックエンドの簡易HTTPサーバはこのヘッダーに対応していないようです。
本来は、「100を返す」「次のリクエストボディを待つ」という動作が必要なのですが、
「100を返さずに」「リクエストボディを待つ」という挙動をしてしまっているようです。
今後、IoT機器などで簡易なHTTPサーバ使われることがあり、こういった事象に出くわすこともあるかもしれませんね。
対策
1日要しました。
Expect: 100-continue
でググって出てくる対策は以下でした。
- APIクライアント側で
Expect: 100-continue
を送らないようにする - HTTPヘッダーから除去
前者はAPIクライアント側が変更不可でした。
後者はリクエストにBODYが含まれていれば有効(=curlで確認)ですが、クライアント側がRFP仕様どおりにボディは空で、100応答を待っている場合は、機能しません。
これはHTTPサーバ(とAPI一式)を変えるしかないのか…。そんな時間ないよ…。と絶望。
原点に戻って、Proxyの設定で何かいいのないかなぁとApacheのドキュメントを眺めていたら、、、
何やら期待できそうなディレクティブがありました。
Proxy100Continue - Apache2.4(公式)
このディレクティブは、プロキシが Expect 100-continue をOriginサーバに転送すべきかどうかを決定します。
これにより、HTTP リクエストボディを読み込むべきかどうか、あるいは、 リクエストボディを転送する前に
プロキシが 100-CONTINUE 中間応答を生成するべきかどうかを決定することができます。
説明がちょっとわかりづらいのですが、この設定のデフォルトはOn
です。
現在の挙動からして、おそらく「Originに転送する」になっています。
するとOff
にすればApacheが変わって100を返してくれて、万事OKになるのでは?
で、試してみたところ、、、ビンゴでした!!
関係者(全員リモートですが)から歓声と安堵のため息が上がりました(たぶん)。
良かった・・。