H2o: H2O.next.call(env)のバグ

作成日 2020年01月20日  ·  7コメント  ·  ソース: h2o/h2o

この問題を「バグ」レポートとして再提出します。これは、最新のソースを使用して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はソースからビルドされています。

全てのコメント7件

エラーメッセージが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.nextH20.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.nextH2O.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_FOOX-FOOではなくX_FOO X-FOOに変換する必要があります。 これがその問題の原因だと思います。サーバーの実装はそれをcontent-typeヘッダーとして認識しません。これは、ほとんどの場合、特別に処理する必要があります。

@ i110どうもありがとう! 明日、あなたのパッチを試してみます。

また、上記のmrubyハンドラーをテストしていただきありがとうございます。 確かに、アンダースコアをハイフンに変更することを完全に忘れていました。 この変更により、意図したとおりに機能し、 CONTENT_TYPECONTENT_TYPEます。

@ i110ありがとうございました! #2254は、 H2O.nextH2O.reprocess両方の問題を解決します。 h2o.confにさらに/pathセグメントを追加して、実際に機能することを確認しました。 そして、はい、そうです!

このページは役に立ちましたか?
0 / 5 - 0 評価