Ngrok开源版本http+https协议配置的调试过程
背景
问题的发生是在我正在做的Bullet项目,需要实现http和https的双协议支持,能够通过相同的域名去兼容访问http和https方式。如下所示:
https://www.joggle.cn
http://www.joggle.cn
由于目前仅支持http或者https单选的方式,我需要去研究Ngrok的源码。于是我就这样配置:
tunnels:
mp_160:
hostname: ''
proto: {
'http+https': '192.168.1.3:5000'
}
inspect: false
subdomain: www
bind_tls: false
...
http+https 是命令行start方式启动支持的,但是通过配置文件启动方式并不支持。
bin/ngrok -config=conf/domain/3weixin.yml -log=stdout start mp_160
这样能运行成功,但是因为不支持,所以直接访问域名www.joggle.cn 是访问不到的。
Ngrok 源码初探
前方沈略很多步骤,我开启了Idea的golang调试模式,于是跟踪到一段代码
client/model.go 328行左右
case *msg.NewTunnel:
if m.Error != "" {
emsg := fmt.Sprintf("Server failed to allocate tunnel: %s", m.Error)
c.Error(emsg)
c.ctl.Shutdown(emsg)
continue
}
// 这里是新的通道会从这里初始化一个Tunnel配置,其中LocalAddr为空字符串
tunnel := mvc.Tunnel{
PublicUrl: m.Url,
LocalAddr: reqIdToTunnelConfig[m.ReqId].Protocols[m.Protocol],
Protocol: c.protoMap[m.Protocol],
}
c.tunnels[tunnel.PublicUrl] = tunnel
c.connStatus = mvc.ConnOnline
c.Info("Tunnel established at %v", tunnel.PublicUrl)
c.update()
注意:LocalAddr是ngrok决定将请求转发到哪个内网的地址IP和端口的配置,格式如:192.168.1.3:5000
。
如何让LocalAddr有值就成为了下一个解决的问题,于是继续跟踪找到了
config.go 117行
// 这里在遍历协议,t.Protocols map的key就是http、https、tcp、http+https
for k, addr := range t.Protocols {
tunnelName := fmt.Sprintf("for tunnel %s[%s]", name, k)
if t.Protocols[k], err = normalizeAddress(addr, tunnelName); err != nil {
return
}
if err = validateProtocol(k, tunnelName); err != nil {
return
}
}
改造代码如下:
// 这里在遍历协议,t.Protocols map的key就是http、https、tcp、http+https
for k, addr := range t.Protocols {
tunnelName := fmt.Sprintf("for tunnel %s[%s]", name, k)
// 切分协议(http+https多协议的情况)
for _, proto := range strings.Split(k, "+"){
// 校验协议是否正确
if err = validateProtocol(proto, tunnelName); err != nil {
return
}
if t.Protocols[proto], err = normalizeAddress(addr, tunnelName); err != nil {
return
}
}
}
然后run起来后,达到了我想要的效果,注入了多个协议的配置,然后就报错了!!!
Server failed to allocate tunnel: The tunnel http://weixin.joggle.cn is already registered.
Server failed to allocate tunnel
相当于重复注册了http协议。
{"Type":"ReqTunnel","Payload":{"ReqId":"141ac667322a442a","Protocol":"http+https+http+https","Hostname":"","Subdomain":"weixin","HttpAuth":"","RemotePort":0}}
[17:06:27 CST 2020/09/30] [DEBG] (ngrok/log.(*PrefixLogger).Debug:79) [ctl:bc18aa6]
相当于协议变了http+https+http+https叠加了好多,产生这个问题的原因是在这里。
client/model.go 291 行
reqTunnel := &msg.ReqTunnel{
ReqId: util.RandId(8),
Protocol: strings.Join(protocols, "+"),
Hostname: config.Hostname,
Subdomain: config.Subdomain,
HttpAuth: config.HttpAuth,
RemotePort: config.RemotePort,
}
方向走错了, 最后找到了正确的配置方式,真实曲折的路线。
下面是http+https的正确配置方式:
tunnels:
mp_160:
hostname: ''
proto: {
'http': '192.168.1.3:5000',
'https': '192.168.1.3:5000'
}
inspect: false
subdomain: www
bind_tls: false
...
接下来就是改造Java的实现代码来支持http和https访问了,当然这些改动会在下一个大版本发布了。
总结
开源的东西大部分是文档不全导致了没有办法真正了解产品的功能,它就像一个黑匣子,你不知道他里面要不断的尝试后得到最终的结果,然后就了解它的特性。
所以开源的项目一定要把文档写好,目前Bullet的文档还是很欠缺没有描述得很完善,所以还需要花一部分时间了完善它。