Apache Camel的简单HTTPS代理服务器

时间:2015-03-11 00:59:49

标签: java https apache-camel http-proxy

我正在尝试使用Apache Camel实现一个简单的HTTP代理服务。 我的代码如下所示:

from("jetty:http://localhost:80?matchOnUriPrefix=true")
.recipientList(simple("jetty:${in.header.CamelHttpUrl}?bridgeEndpoint=true&throwExceptionOnFailure=false&disableStreamCache=true"));

它基本上是this,带有动态收件人列表以支持多个目的地。我还必须添加disableStreamCache=true位,否则我会得到奇怪的路径重复异常(例如/index.html会成为/index.html/index.html)。

然而,它似乎有效。但只有HTTP请求。当我尝试访问HTTPS站点时,我总是得到404。

根据日志,jetty组件似乎找不到远程服务器。我不明白为什么。

01:36:37.495 [qtp85415531-22 - www.google.cz:443] DEBUG org.eclipse.jetty.server.Server - REQUEST www.google.cz:443 on AsyncHttpConnection@6964b063,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-5,l=17,c=0},r=1
01:36:37.495 [qtp85415531-22 - www.google.cz:443] DEBUG o.e.j.server.handler.ContextHandler - scope null||www.google.cz:443 @ o.e.j.s.ServletContextHandler{/,null}
01:36:37.495 [qtp85415531-22 - www.google.cz:443] DEBUG o.e.j.server.handler.ContextHandler - context=null||www.google.cz:443 @ o.e.j.s.ServletContextHandler{/,null}
01:36:37.495 [qtp85415531-22 - www.google.cz:443] DEBUG o.e.jetty.servlet.ServletHandler - servlet null||www.google.cz:443 -> null
01:36:37.495 [qtp85415531-22 - www.google.cz:443] DEBUG o.e.jetty.servlet.ServletHandler - chain=null
01:36:37.495 [qtp85415531-22 - www.google.cz:443] DEBUG o.e.jetty.servlet.ServletHandler - Not Found www.google.cz:443
01:36:37.495 [qtp85415531-22 - www.google.cz:443] DEBUG org.eclipse.jetty.server.Server - RESPONSE www.google.cz:443  200 handled=false

如何启用此HTTPS支持?是否可以使用标准的Camel组件?

修改

我设法将路由定义更新为不使用收件人列表。我不知道这是否能提高性能(是吗?)但感觉更好。我还能够在不使用disableStreamCache=true的情况下删除路径重复问题。

from("jetty:http://localhost:80?matchOnUriPrefix=true")
.to("http4:dummy?bridgeEndpoint=true&urlRewrite=#urlRewrite&throwExceptionOnFailure=false");

URL重写器实现:

UrlRewrite urlRewrite = new HttpServletUrlRewrite() {
    @Override
    public String rewrite(String url, String relativeUrl, Producer producer, HttpServletRequest request) throws Exception {
        return request.getRequestURL().toString();
    }

    @Override
    public String rewrite(String url, String relativeUrl, Producer producer) throws Exception {
        return null;
    }
};

编辑2:

我应该提一下,我想拦截这些请求并读取/更改内容(实际上只有HTTP标头)。实际上我想实现一个MITM代理。

编辑3:

我尝试用log替换目标组件,以查看请求是否通过:

from("jetty:http://localhost:80?matchOnUriPrefix=true")
.to("log:test")

当用作HTTP代理时,会记录该消息。当我用jetty:https://localhost?matchOnUriPrefix=true替换URI时,它也会被记录,并尝试直接在浏览器中打开https://localhost。但是,当尝试将其用作HTTPS的代理时,我无法将其记录下来。看起来Jetty组件不支持此行为。这是对的吗?

我也尝试使用具有类似结果的Netty-http组件(路由跟踪器记录了CONNECT请求,但消息未传递给Log组件)

2 个答案:

答案 0 :(得分:3)

关键是要为jetty定义一个处理CONNECT方法的处理程序:

from("jetty:http://localhost:80?matchOnUriPrefix=true&handlers=#connectHandler")

在这种情况下,connectHandler可以是Jetty ConnectHandler(对于普通的HTTPS代理)或自定义的(对于MITM代理):

class CustomConnectHandler extends ConnectHandler {
    @Override
    protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException {
        SocketChannel channel = SocketChannel.open();
        channel.socket().setTcpNoDelay(true);
        channel.socket().connect(new InetSocketAddress("localhost", 443), getConnectTimeout());
        return channel;
    }
}

这个连接到localhost上的端口443并在那里路由SSL数据。所以我们需要从那里定义另一条路线:

from("jetty:https://localhost:443?matchOnUriPrefix=true")

(我们可以保留两个单独的路线,或者稍后通过使用SEDA组件加入它们)

要定义自定义SSL上下文参数(密钥库,信任库等),我们可以在端点上使用sslContextParametersRef参数。

接下来,我们需要更改网址。我们通过使用完整URL填充URI标头(默认情况下它只是相对路径):

.setHeader(Exchange.HTTP_URI, simple("${header.CamelHttpUrl}"))

我们还需要删除PATH标头,因为HTTP4组件通过连接URI和PATH标头来构建最终的URL:

.removeHeader(Exchange.HTTP_PATH)

最后,我们可以将消息转发到HTTP4组件。我们还应该设置throwExceptionOnFailure=false将错误转发给客户端,并httpClient.redirectsEnabled=false转发重定向(否则会导致部分网站混乱):

.to("http4:dummy?throwExceptionOnFailure=false&httpClient.redirectsEnabled=false");

我希望这可以帮助任何想要使用Camel实现HTTPS代理并且像我一样苦苦挣扎的人。

答案 1 :(得分:0)

在大多数计算机系统上,localhost解析为IP地址127.0.0.1

注意: - 这是使用Fiddler的有效实现。***

<camelContext id="context" xmlns="http://camel.apache.org/schema/spring">
    <properties>
        <property key="http.proxyHost" value="127.0.0.1"/>
        <property key="http.proxyPort" value="8888"/>
   </properties>
</camelContext>