この問題を「バグ」レポートとして再提出します。これは、最新のソースを使用してFedoraでもテストしたためです。
このバグのデモンストレーションには、次のh2o.confを使用できます。
user: www
access-log: /var/log/h2o/h2o-access.log
error-log: /var/log/h2o/h2o-error.log
listen: 8080
hosts:
"<my-ip-address-here>:8080":
paths:
"/dir0":
mruby.handler: |
proc {|env|
resp = H2O.next.call(env)
resp
}
proxy.reverse.url: "https://www.google.com" # this URL is just for testing/demonstration
"/dir1":
file.dir: "/usr/local/www/data/testh2o/dir1"
"/dir2":
file.dir: "/usr/local/www/data/testh2o/dir2"
"/":
file.dir: "/usr/local/www/data/testh2o"
これは機能します:dir1、dir2、dir3の下のファイルにアクセスできます。 そして、 http://<my-ip-address-here>:8080/dir0
をリクエストすると、Googleホームページが表示されます(デモ用)。
ただし、ここでHardenedBSD、FreeBSD/path
セグメントを追加すると、H20.next.call(env)が壊れます。
壊れたh2o.conf:
user: www
access-log: /var/log/h2o/h2o-access.log
error-log: /var/log/h2o/h2o-error.log
listen: 8080
hosts:
"<my-ip-address-here>:8080":
paths:
"/dir0":
mruby.handler: |
proc {|env|
resp = H2O.next.call(env)
# In my production conf I have here code to look for a certain header field.
# If it finds it, this handler makes a http_request() to a certain URL on another server
# in order to trigger emptying a cache.
# This bug demo is independent of this code.
resp
}
proxy.reverse.url: "https://www.google.com" # this URL is just for testing/demonstration
"/dir1":
file.dir: "/usr/local/www/data/testh2o/dir1"
"/dir2":
file.dir: "/usr/local/www/data/testh2o/dir2"
"/dir3":
file.dir: "/usr/local/www/data/testh2o/dir3"
"/":
file.dir: "/usr/local/www/data/testh2o"
内部サーバーエラーが発生します。 そして、Fedoraのエラーログには次のように書かれています。
[h2o_mruby] in request:/dir0:mruby raised: (eval):28: can't modify `SCRIPT_NAME` with `H2O.next`. Is `H2O.reprocess` what you want? (RuntimeError)
これは新しいFedora31にあり、H2Oはソースからビルドされています。
エラーメッセージがH2O.reprocess
使用を提案したので、私は今このオプションを試しました。 同じ結果:パスセグメントを追加すると、h2oがクラッシュします。
hosts:
test.local:
listen:
port: 80
host: 10.0.0.10
paths:
"/dir0":
file.dir: "/usr/local/www/data/testh2o/dir0"
"/dir1":
file.dir: "/usr/local/www/data/testh2o/dir1"
"/wp-admin":
mruby.handler: |
proc {|env|
env['SCRIPT_NAME'] = '/proxy-wp-admin'
resp = H2O.reprocess.call(env)
resp
}
"/proxy-wp-admin":
proxy.reverse.url: "https://www.google.com"
この構成では、ブラウザでhttp://test.local/wp-admin
を開くと、Googleホームページが表示されます。
ただし、パスセグメントをもう1つ追加すると、たとえば/dir2
を追加すると、次のエラーが発生します。
Feb 4 17:12:11 web2 h2o[34490]: received fatal signal 11
Feb 4 17:12:11 web2 h2o[34490]: [34493] 0x4cf6a0 <???> at /usr/local/bin/h2o
Feb 4 17:12:11 web2 h2o[34490]: [34493] 0x8018c5946 <pthread_sigmask+0x536> at /lib/libthr.so.3
Feb 4 17:12:11 web2 h2o[34490]: [34493] 0x8018c4eb2 <pthread_getspecific+0xe12> at /lib/libthr.so.3
許可されるパスセグメントの数に本当にこのような下限はありますか? これはバグに違いありません。 おそらくH2O.next
のものと同じです。
H2O.next
とH20.reprocess
両方が壊れているように見えるので、別の解決策を検討する必要がありました。 @kazuhoの例に従って、mrubyにリバースプロキシハンドラーを実装しました。
私のテストサーバーでは、これは機能します。 間もなく本番環境にデプロイする予定です。 多分それは他の誰かが上記の方法で同じ問題に遭遇するのを助けます:
proc {|env|
# copy headers
headers = {}
env.each do |key, value|
if /^HTTP_/.match(key)
headers[$'] = value
end
end
if env['CONTENT_TYPE']
headers['CONTENT_TYPE'] = env['CONTENT_TYPE']
end
# issue the request
input = env["rack.input"] ? env["rack.input"] : ""
if env['QUERY_STRING'].to_s != ''
uri = env['SCRIPT_NAME'] + env['PATH_INFO'] + '?' + env['QUERY_STRING']
else
uri = env['SCRIPT_NAME'] + env['PATH_INFO']
end
req = http_request(
"http://<backend-ip>#{uri}",
method: env["REQUEST_METHOD"],
headers: headers,
body: input,
)
# Extract status, headers and body, so that I can look into the headers
status, headers, body = req.join
# actual work done here, if a certain header is present
[status, headers, body]
}
まだ完全には機能していません:
H2Oまたはmrubyは、元のCONTENT_TYPE
ヘッダーをHTTP_CONTENT_TYPE
に変換します。これにより、WordPressバックエンドが破損します...
例:
"CONTENT_TYPE"=>"application/x-www-form-urlencoded; charset=UTF-8"
になります"HTTP_CONTENT_TYPE"=>"application/x-www-form-urlencoded; charset=UTF-8"
CONTENT_TYPE
だけを使用するように強制する方法はありますか?
そして、これはすべて、h2o.confに別の/path
を追加したかったからです...
@utrenkner遅れてすみません。 https://github.com/h2o/h2o/pull/2254で問題が解決することを願っていH2O.next
とH2O.reprocess
はうまくいくと思います
H2Oまたはmrubyは、元のCONTENT_TYPEヘッダーをHTTP_CONTENT_TYPEに変換します。これにより、WordPressバックエンドが破損します...
同じmrubyハンドラーを試しましたが、再現できませんでした。 つまり、最小限のアップストリームサーバー( echo -ne "HTTP/1.1 200 OK\r\n\r\n" | nc -l 9000
)を生成し、h2oから送信されたヘッダーを確認します。 それは含まCONTENT_TYPE
、ないHTTP_CONTENT_TYPE
。
ところで、このhttp_requestアプローチを採用する場合は、ヘッダーハッシュキーのアンダースコアをハイフンに変換する必要があります。つまり、 HTTP_X_FOO
をX-FOO
ではなくX_FOO
X-FOO
に変換する必要があります。 これがその問題の原因だと思います。サーバーの実装はそれをcontent-type
ヘッダーとして認識しません。これは、ほとんどの場合、特別に処理する必要があります。
@ i110どうもありがとう! 明日、あなたのパッチを試してみます。
また、上記のmrubyハンドラーをテストしていただきありがとうございます。 確かに、アンダースコアをハイフンに変更することを完全に忘れていました。 この変更により、意図したとおりに機能し、 CONTENT_TYPE
はCONTENT_TYPE
ます。
@ i110ありがとうございました! #2254は、 H2O.next
とH2O.reprocess
両方の問題を解決します。 h2o.confにさらに/path
セグメントを追加して、実際に機能することを確認しました。 そして、はい、そうです!