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 主页(仅用于演示)。

但是再添加一个/path会破坏 H20.next.call(env),如此处针对 HardenedBSD、FreeBSD 所述。

损坏的 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)

这是在新的 Fedora 31 上,从b9989220bea9bda30f69083b66166c4c657bdf84 的源代码构建的 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给我谷歌主页。

但是再添加一个路径段,例如一个额外的/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

允许的路径段数量真的有这么低的限制吗??? 这一定是个bug。 可能与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能解决这个问题。 有了这个 PR 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_FOO转换X-FOO ,而不是X_FOO 。 我想这是该问题的原因:您的服务器实现无法将其识别为content-type标头,在大多数情况下必须对其进行特殊处理。

@i110非常感谢! 明天我会试试你的补丁。

也感谢您测试上述 mruby 处理程序。 确实,我完全忘记了将下划线更改为连字符。 通过这种更改,它确实按预期工作,并且CONTENT_TYPE保持CONTENT_TYPE

@i110 非常感谢! #2254 用H2O.nextH2O.reprocess解决了我的问题。 我在 h2o.conf 中添加了更多的/path段,只是为了检查它是否真的有效。 而且,是的,确实如此!

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

ndac-todoroki picture ndac-todoroki  ·  5评论

utrenkner picture utrenkner  ·  3评论

concatime picture concatime  ·  3评论

voiddeveloper picture voiddeveloper  ·  6评论

kazuho picture kazuho  ·  7评论