使用 Apache Camel 的简单 HTTPS 代理服务器
A simple HTTPS proxy server with Apache Camel
我正在尝试使用 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 组件甚至可能吗?
编辑:
我设法将我的路由定义更新为不使用收件人列表。我不知道这是否会改善任何事情 performance-wise(是吗?)但感觉好多了。通过这个,我还能够在不使用 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:
我应该提一下,我想拦截那些请求和 read/alter 内容(实际上只有 HTTP headers)。实际上我想实现一个 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
请求,但消息没有传递到日志组件)
关键是为码头定义一个处理程序来处理 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;
}
}
这个连接到本地主机上的端口 443 并在那里路由 SSL 数据。所以我们需要从那里定义另一条路线:
from("jetty:https://localhost:443?matchOnUriPrefix=true")
(我们可以保留两条单独的路线,也可以稍后使用 SEDA 组件加入它们)
要定义自定义 SSL 上下文参数(密钥库、信任库等),我们可以在端点上使用 sslContextParametersRef
参数。
接下来,我们需要修改URL。我们通过使用完整的 URL 填充 URI header 来实现这一点(默认情况下它只是相对路径):
.setHeader(Exchange.HTTP_URI, simple("${header.CamelHttpUrl}"))
我们还需要删除 PATH header,因为 HTTP4 组件通过连接 URI 和 PATH headers:
来构建最终的 URL
.removeHeader(Exchange.HTTP_PATH)
最后,我们可以将消息转发给 HTTP4 组件。我们还应该设置 throwExceptionOnFailure=false
以将错误转发给客户端,并设置 httpClient.redirectsEnabled=false
以转发重定向(否则这可能会混淆某些网站):
.to("http4:dummy?throwExceptionOnFailure=false&httpClient.redirectsEnabled=false");
我希望这对任何想使用 Camel 实现 HTTPS 代理并且像我一样苦苦挣扎的人有所帮助。
在大多数计算机系统上,本地主机解析为 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>
我正在尝试使用 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 组件甚至可能吗?
编辑:
我设法将我的路由定义更新为不使用收件人列表。我不知道这是否会改善任何事情 performance-wise(是吗?)但感觉好多了。通过这个,我还能够在不使用 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:
我应该提一下,我想拦截那些请求和 read/alter 内容(实际上只有 HTTP headers)。实际上我想实现一个 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
请求,但消息没有传递到日志组件)
关键是为码头定义一个处理程序来处理 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;
}
}
这个连接到本地主机上的端口 443 并在那里路由 SSL 数据。所以我们需要从那里定义另一条路线:
from("jetty:https://localhost:443?matchOnUriPrefix=true")
(我们可以保留两条单独的路线,也可以稍后使用 SEDA 组件加入它们)
要定义自定义 SSL 上下文参数(密钥库、信任库等),我们可以在端点上使用 sslContextParametersRef
参数。
接下来,我们需要修改URL。我们通过使用完整的 URL 填充 URI header 来实现这一点(默认情况下它只是相对路径):
.setHeader(Exchange.HTTP_URI, simple("${header.CamelHttpUrl}"))
我们还需要删除 PATH header,因为 HTTP4 组件通过连接 URI 和 PATH headers:
来构建最终的 URL.removeHeader(Exchange.HTTP_PATH)
最后,我们可以将消息转发给 HTTP4 组件。我们还应该设置 throwExceptionOnFailure=false
以将错误转发给客户端,并设置 httpClient.redirectsEnabled=false
以转发重定向(否则这可能会混淆某些网站):
.to("http4:dummy?throwExceptionOnFailure=false&httpClient.redirectsEnabled=false");
我希望这对任何想使用 Camel 实现 HTTPS 代理并且像我一样苦苦挣扎的人有所帮助。
在大多数计算机系统上,本地主机解析为 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>