mirror of
https://github.com/fatedier/frp.git
synced 2026-04-05 08:39:17 +08:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c008b14d0f | ||
|
|
853892f3cd | ||
|
|
e43f9f5850 | ||
|
|
d5f30ccd6b | ||
|
|
b87df569e7 | ||
|
|
976cf3e9f8 | ||
|
|
371c401f5b | ||
|
|
69919e8ef9 | ||
|
|
9abbe33790 | ||
|
|
4a5c00286e | ||
|
|
dfb892c8f6 | ||
|
|
461c4c18fd | ||
|
|
00b9ba95ae | ||
|
|
c47aad348d | ||
|
|
4cb4da3afc | ||
|
|
c1f57da00d | ||
|
|
fe187eb8ec | ||
|
|
0f6f674a64 | ||
|
|
814afbe1f6 | ||
|
|
3fde9176c9 | ||
|
|
af7cca1a93 | ||
|
|
7dd28a14aa | ||
|
|
1325c59a4c | ||
|
|
82dc1e924f | ||
|
|
3166bdf3f0 | ||
|
|
8af70c8822 | ||
|
|
87763e8251 | ||
|
|
e9241aeb94 | ||
|
|
2eaf134042 | ||
|
|
1739e012d6 | ||
|
|
9e8980429f | ||
|
|
1d0865ca49 | ||
|
|
5c9909aeef | ||
|
|
456ce09061 | ||
|
|
ffc13b704a | ||
|
|
5d239127bb | ||
|
|
9b990adf96 | ||
|
|
44e8108910 | ||
|
|
1c35e9a0c6 | ||
|
|
8e719ff0ff | ||
|
|
637ddbce1f | ||
|
|
ce8fde793c | ||
|
|
eede31c064 | ||
|
|
41c41789b6 | ||
|
|
68dfc89bce |
@@ -2,8 +2,7 @@ sudo: false
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.8.x
|
- 1.10.x
|
||||||
- 1.9.x
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- make
|
- make
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.8
|
FROM golang:1.10
|
||||||
|
|
||||||
COPY . /go/src/github.com/fatedier/frp
|
COPY . /go/src/github.com/fatedier/frp
|
||||||
|
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -15,12 +15,7 @@ file:
|
|||||||
go generate ./assets/...
|
go generate ./assets/...
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt ./assets/...
|
go fmt ./...
|
||||||
go fmt ./client/...
|
|
||||||
go fmt ./cmd/...
|
|
||||||
go fmt ./models/...
|
|
||||||
go fmt ./server/...
|
|
||||||
go fmt ./utils/...
|
|
||||||
|
|
||||||
frps:
|
frps:
|
||||||
go build -o bin/frps ./cmd/frps
|
go build -o bin/frps ./cmd/frps
|
||||||
@@ -44,7 +39,7 @@ ci:
|
|||||||
go test -v ./tests/...
|
go test -v ./tests/...
|
||||||
cd ./tests && ./clean_test.sh && cd -
|
cd ./tests && ./clean_test.sh && cd -
|
||||||
|
|
||||||
ciclean:
|
cic:
|
||||||
cd ./tests && ./clean_test.sh && cd -
|
cd ./tests && ./clean_test.sh && cd -
|
||||||
|
|
||||||
alltest: gotest ci
|
alltest: gotest ci
|
||||||
@@ -53,6 +48,3 @@ clean:
|
|||||||
rm -f ./bin/frpc
|
rm -f ./bin/frpc
|
||||||
rm -f ./bin/frps
|
rm -f ./bin/frps
|
||||||
cd ./tests && ./clean_test.sh && cd -
|
cd ./tests && ./clean_test.sh && cd -
|
||||||
|
|
||||||
save:
|
|
||||||
godep save ./...
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ build: app
|
|||||||
app:
|
app:
|
||||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_darwin_amd64 ./cmd/frpc
|
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_darwin_amd64 ./cmd/frpc
|
||||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_darwin_amd64 ./cmd/frps
|
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_darwin_amd64 ./cmd/frps
|
||||||
|
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_386 ./cmd/frpc
|
||||||
|
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_386 ./cmd/frps
|
||||||
|
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_amd64 ./cmd/frpc
|
||||||
|
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_amd64 ./cmd/frps
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_386 ./cmd/frpc
|
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_386 ./cmd/frpc
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_386 ./cmd/frps
|
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_386 ./cmd/frps
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_amd64 ./cmd/frpc
|
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_amd64 ./cmd/frpc
|
||||||
@@ -23,10 +27,10 @@ app:
|
|||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64 ./cmd/frps
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64 ./cmd/frps
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips64le ./cmd/frpc
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips64le ./cmd/frpc
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64le ./cmd/frps
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64le ./cmd/frps
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips ./cmd/frpc
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips ./cmd/frpc
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips ./cmd/frps
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips ./cmd/frps
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mipsle ./cmd/frpc
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mipsle ./cmd/frpc
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mipsle ./cmd/frps
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mipsle ./cmd/frps
|
||||||
|
|
||||||
temp:
|
temp:
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_amd64 ./cmd/frps
|
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_amd64 ./cmd/frps
|
||||||
|
|||||||
68
README.md
68
README.md
@@ -20,6 +20,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
|||||||
* [Visit your web service in LAN by custom domains](#visit-your-web-service-in-lan-by-custom-domains)
|
* [Visit your web service in LAN by custom domains](#visit-your-web-service-in-lan-by-custom-domains)
|
||||||
* [Forward DNS query request](#forward-dns-query-request)
|
* [Forward DNS query request](#forward-dns-query-request)
|
||||||
* [Forward unix domain socket](#forward-unix-domain-socket)
|
* [Forward unix domain socket](#forward-unix-domain-socket)
|
||||||
|
* [Expose a simple http file server](#expose-a-simple-http-file-server)
|
||||||
* [Expose your service in security](#expose-your-service-in-security)
|
* [Expose your service in security](#expose-your-service-in-security)
|
||||||
* [P2P Mode](#p2p-mode)
|
* [P2P Mode](#p2p-mode)
|
||||||
* [Connect website through frpc's network](#connect-website-through-frpcs-network)
|
* [Connect website through frpc's network](#connect-website-through-frpcs-network)
|
||||||
@@ -30,8 +31,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
|||||||
* [Encryption and Compression](#encryption-and-compression)
|
* [Encryption and Compression](#encryption-and-compression)
|
||||||
* [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
|
* [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
|
||||||
* [Get proxy status from client](#get-proxy-status-from-client)
|
* [Get proxy status from client](#get-proxy-status-from-client)
|
||||||
* [Privilege Mode](#privilege-mode)
|
* [Port White List](#port-white-list)
|
||||||
* [Port White List](#port-white-list)
|
|
||||||
* [TCP Stream Multiplexing](#tcp-stream-multiplexing)
|
* [TCP Stream Multiplexing](#tcp-stream-multiplexing)
|
||||||
* [Support KCP Protocol](#support-kcp-protocol)
|
* [Support KCP Protocol](#support-kcp-protocol)
|
||||||
* [Connection Pool](#connection-pool)
|
* [Connection Pool](#connection-pool)
|
||||||
@@ -41,6 +41,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
|||||||
* [Custom subdomain names](#custom-subdomain-names)
|
* [Custom subdomain names](#custom-subdomain-names)
|
||||||
* [URL routing](#url-routing)
|
* [URL routing](#url-routing)
|
||||||
* [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
|
* [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
|
||||||
|
* [Range ports mapping](#range-ports-mapping)
|
||||||
* [Plugin](#plugin)
|
* [Plugin](#plugin)
|
||||||
* [Development Plan](#development-plan)
|
* [Development Plan](#development-plan)
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
@@ -214,6 +215,32 @@ Configure frps same as above.
|
|||||||
|
|
||||||
`curl http://x.x.x.x:6000/version`
|
`curl http://x.x.x.x:6000/version`
|
||||||
|
|
||||||
|
### Expose a simple http file server
|
||||||
|
|
||||||
|
A simple way to visit files in the LAN.
|
||||||
|
|
||||||
|
Configure frps same as above.
|
||||||
|
|
||||||
|
1. Start frpc with configurations:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# frpc.ini
|
||||||
|
[common]
|
||||||
|
server_addr = x.x.x.x
|
||||||
|
server_port = 7000
|
||||||
|
|
||||||
|
[test_static_file]
|
||||||
|
type = tcp
|
||||||
|
remote_port = 6000
|
||||||
|
plugin = static_file
|
||||||
|
plugin_local_path = /tmp/file
|
||||||
|
plugin_strip_prefix = static
|
||||||
|
plugin_http_user = abc
|
||||||
|
plugin_http_passwd = abc
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Visit `http://x.x.x.x:6000/static/` by your browser, set correct user and password, so you can see files in `/tmp/file`.
|
||||||
|
|
||||||
### Expose your service in security
|
### Expose your service in security
|
||||||
|
|
||||||
For some services, if expose them to the public network directly will be a security risk.
|
For some services, if expose them to the public network directly will be a security risk.
|
||||||
@@ -356,7 +383,7 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa
|
|||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
Since v0.10.0, you only need to set `privilege_token` in frps.ini and frpc.ini.
|
Since v0.10.0, you only need to set `token` in frps.ini and frpc.ini.
|
||||||
|
|
||||||
Note that time duration between server of frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
|
Note that time duration between server of frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
|
||||||
|
|
||||||
@@ -395,21 +422,17 @@ Then run command `frpc reload -c ./frpc.ini` and wait for about 10 seconds to le
|
|||||||
|
|
||||||
Use `frpc status -c ./frpc.ini` to get status of all proxies. You need to set admin port in frpc's configure file.
|
Use `frpc status -c ./frpc.ini` to get status of all proxies. You need to set admin port in frpc's configure file.
|
||||||
|
|
||||||
### Privilege Mode
|
### Port White List
|
||||||
|
|
||||||
Privilege mode is the default and only mode support in frp since v0.10.0. All proxy configurations are set in client.
|
`allow_ports` in frps.ini is used for preventing abuse of ports:
|
||||||
|
|
||||||
#### Port White List
|
|
||||||
|
|
||||||
`privilege_allow_ports` in frps.ini is used for preventing abuse of ports:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# frps.ini
|
# frps.ini
|
||||||
[common]
|
[common]
|
||||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
```
|
```
|
||||||
|
|
||||||
`privilege_allow_ports` consists of a specific port or a range of ports divided by `,`.
|
`allow_ports` consists of a specific port or a range of ports divided by `,`.
|
||||||
|
|
||||||
### TCP Stream Multiplexing
|
### TCP Stream Multiplexing
|
||||||
|
|
||||||
@@ -512,7 +535,7 @@ type = http
|
|||||||
local_port = 80
|
local_port = 80
|
||||||
custom_domains = test.yourdomain.com
|
custom_domains = test.yourdomain.com
|
||||||
http_user = abc
|
http_user = abc
|
||||||
http_pwd = abc
|
http_passwd = abc
|
||||||
```
|
```
|
||||||
|
|
||||||
Visit `http://test.yourdomain.com` and now you need to input username and password.
|
Visit `http://test.yourdomain.com` and now you need to input username and password.
|
||||||
@@ -576,11 +599,26 @@ server_port = 7000
|
|||||||
http_proxy = http://user:pwd@192.168.1.128:8080
|
http_proxy = http://user:pwd@192.168.1.128:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Range ports mapping
|
||||||
|
|
||||||
|
Proxy name has prefix `range:` will support mapping range ports.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# frpc.ini
|
||||||
|
[range:test_tcp]
|
||||||
|
type = tcp
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = 6000-6006,6007
|
||||||
|
remote_port = 6000-6006,6007
|
||||||
|
```
|
||||||
|
|
||||||
|
frpc will generate 6 proxies like `test_tcp_0, test_tcp_1 ... test_tcp_5`.
|
||||||
|
|
||||||
### Plugin
|
### Plugin
|
||||||
|
|
||||||
frpc only forward request to local tcp or udp port by default.
|
frpc only forward request to local tcp or udp port by default.
|
||||||
|
|
||||||
Plugin is used for providing rich features. There are built-in plugins such as **unix_domain_socket**, **http_proxy**, **socks5** and you can see [example usage](#example-usage).
|
Plugin is used for providing rich features. There are built-in plugins such as `unix_domain_socket`, `http_proxy`, `socks5`, `static_file` and you can see [example usage](#example-usage).
|
||||||
|
|
||||||
Specify which plugin to use by `plugin` parameter. Configuration parameters of plugin should be started with `plugin_`. `local_ip` and `local_port` is useless for plugin.
|
Specify which plugin to use by `plugin` parameter. Configuration parameters of plugin should be started with `plugin_`. `local_ip` and `local_port` is useless for plugin.
|
||||||
|
|
||||||
@@ -598,17 +636,13 @@ plugin_http_passwd = abc
|
|||||||
|
|
||||||
`plugin_http_user` and `plugin_http_passwd` are configuration parameters used in `http_proxy` plugin.
|
`plugin_http_user` and `plugin_http_passwd` are configuration parameters used in `http_proxy` plugin.
|
||||||
|
|
||||||
|
|
||||||
## Development Plan
|
## Development Plan
|
||||||
|
|
||||||
* Log http request information in frps.
|
* Log http request information in frps.
|
||||||
* Direct reverse proxy, like haproxy.
|
* Direct reverse proxy, like haproxy.
|
||||||
* Load balance to different service in frpc.
|
* Load balance to different service in frpc.
|
||||||
* Frpc can directly be a webserver for static files.
|
|
||||||
* P2p communicate by making udp hole to penetrate NAT.
|
|
||||||
* kubernetes ingress support.
|
* kubernetes ingress support.
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Interested in getting involved? We would like to help you!
|
Interested in getting involved? We would like to help you!
|
||||||
|
|||||||
74
README_zh.md
74
README_zh.md
@@ -18,6 +18,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
|||||||
* [通过自定义域名访问部署于内网的 web 服务](#通过自定义域名访问部署于内网的-web-服务)
|
* [通过自定义域名访问部署于内网的 web 服务](#通过自定义域名访问部署于内网的-web-服务)
|
||||||
* [转发 DNS 查询请求](#转发-dns-查询请求)
|
* [转发 DNS 查询请求](#转发-dns-查询请求)
|
||||||
* [转发 Unix域套接字](#转发-unix域套接字)
|
* [转发 Unix域套接字](#转发-unix域套接字)
|
||||||
|
* [对外提供简单的文件访问服务](#对外提供简单的文件访问服务)
|
||||||
* [安全地暴露内网服务](#安全地暴露内网服务)
|
* [安全地暴露内网服务](#安全地暴露内网服务)
|
||||||
* [点对点内网穿透](#点对点内网穿透)
|
* [点对点内网穿透](#点对点内网穿透)
|
||||||
* [通过 frpc 所在机器访问外网](#通过-frpc-所在机器访问外网)
|
* [通过 frpc 所在机器访问外网](#通过-frpc-所在机器访问外网)
|
||||||
@@ -28,8 +29,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
|||||||
* [加密与压缩](#加密与压缩)
|
* [加密与压缩](#加密与压缩)
|
||||||
* [客户端热加载配置文件](#客户端热加载配置文件)
|
* [客户端热加载配置文件](#客户端热加载配置文件)
|
||||||
* [客户端查看代理状态](#客户端查看代理状态)
|
* [客户端查看代理状态](#客户端查看代理状态)
|
||||||
* [特权模式](#特权模式)
|
* [端口白名单](#端口白名单)
|
||||||
* [端口白名单](#端口白名单)
|
|
||||||
* [TCP 多路复用](#tcp-多路复用)
|
* [TCP 多路复用](#tcp-多路复用)
|
||||||
* [底层通信可选 kcp 协议](#底层通信可选-kcp-协议)
|
* [底层通信可选 kcp 协议](#底层通信可选-kcp-协议)
|
||||||
* [连接池](#连接池)
|
* [连接池](#连接池)
|
||||||
@@ -39,6 +39,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
|||||||
* [自定义二级域名](#自定义二级域名)
|
* [自定义二级域名](#自定义二级域名)
|
||||||
* [URL 路由](#url-路由)
|
* [URL 路由](#url-路由)
|
||||||
* [通过代理连接 frps](#通过代理连接-frps)
|
* [通过代理连接 frps](#通过代理连接-frps)
|
||||||
|
* [范围端口映射](#范围端口映射)
|
||||||
* [插件](#插件)
|
* [插件](#插件)
|
||||||
* [开发计划](#开发计划)
|
* [开发计划](#开发计划)
|
||||||
* [为 frp 做贡献](#为-frp-做贡献)
|
* [为 frp 做贡献](#为-frp-做贡献)
|
||||||
@@ -192,11 +193,11 @@ DNS 查询请求通常使用 UDP 协议,frp 支持对内网 UDP 服务的穿
|
|||||||
|
|
||||||
### 转发 Unix域套接字
|
### 转发 Unix域套接字
|
||||||
|
|
||||||
通过 tcp 端口访问内网的 unix域套接字(和 docker daemon 通信)。
|
通过 tcp 端口访问内网的 unix域套接字(例如和 docker daemon 通信)。
|
||||||
|
|
||||||
frps 的部署步骤同上。
|
frps 的部署步骤同上。
|
||||||
|
|
||||||
1. 启动 frpc,启用 unix_domain_socket 插件,配置如下:
|
1. 启动 frpc,启用 `unix_domain_socket` 插件,配置如下:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# frpc.ini
|
# frpc.ini
|
||||||
@@ -215,6 +216,34 @@ frps 的部署步骤同上。
|
|||||||
|
|
||||||
`curl http://x.x.x.x:6000/version`
|
`curl http://x.x.x.x:6000/version`
|
||||||
|
|
||||||
|
### 对外提供简单的文件访问服务
|
||||||
|
|
||||||
|
通过 `static_file` 插件可以对外提供一个简单的基于 HTTP 的文件访问服务。
|
||||||
|
|
||||||
|
frps 的部署步骤同上。
|
||||||
|
|
||||||
|
1. 启动 frpc,启用 `static_file` 插件,配置如下:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# frpc.ini
|
||||||
|
[common]
|
||||||
|
server_addr = x.x.x.x
|
||||||
|
server_port = 7000
|
||||||
|
|
||||||
|
[test_static_file]
|
||||||
|
type = tcp
|
||||||
|
remote_port = 6000
|
||||||
|
plugin = static_file
|
||||||
|
# 要对外暴露的文件目录
|
||||||
|
plugin_local_path = /tmp/file
|
||||||
|
# 访问 url 中会被去除的前缀,保留的内容即为要访问的文件路径
|
||||||
|
plugin_strip_prefix = static
|
||||||
|
plugin_http_user = abc
|
||||||
|
plugin_http_passwd = abc
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 通过浏览器访问 `http://x.x.x.x:6000/static/` 来查看位于 `/tmp/file` 目录下的文件,会要求输入已设置好的用户名和密码。
|
||||||
|
|
||||||
### 安全地暴露内网服务
|
### 安全地暴露内网服务
|
||||||
|
|
||||||
对于某些服务来说如果直接暴露于公网上将会存在安全隐患。
|
对于某些服务来说如果直接暴露于公网上将会存在安全隐患。
|
||||||
@@ -371,7 +400,7 @@ dashboard_pwd = admin
|
|||||||
|
|
||||||
### 身份验证
|
### 身份验证
|
||||||
|
|
||||||
从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `privilege_token` 参数一致则身份验证通过。
|
从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `token` 参数一致则身份验证通过。
|
||||||
|
|
||||||
需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
|
需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
|
||||||
|
|
||||||
@@ -420,21 +449,17 @@ admin_port = 7400
|
|||||||
|
|
||||||
frpc 支持通过 `frpc status -c ./frpc.ini` 命令查看代理的状态信息,此功能需要在 frpc 中配置 admin 端口。
|
frpc 支持通过 `frpc status -c ./frpc.ini` 命令查看代理的状态信息,此功能需要在 frpc 中配置 admin 端口。
|
||||||
|
|
||||||
### 特权模式
|
### 端口白名单
|
||||||
|
|
||||||
由于从 v0.10.0 版本开始,所有 proxy 都在客户端配置,原先的特权模式是目前唯一支持的模式。
|
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 `allow_ports` 来指定:
|
||||||
|
|
||||||
#### 端口白名单
|
|
||||||
|
|
||||||
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 privilege_allow_ports 来指定:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# frps.ini
|
# frps.ini
|
||||||
[common]
|
[common]
|
||||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
```
|
```
|
||||||
|
|
||||||
privilege_allow_ports 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
`allow_ports` 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
||||||
|
|
||||||
### TCP 多路复用
|
### TCP 多路复用
|
||||||
|
|
||||||
@@ -609,11 +634,30 @@ server_port = 7000
|
|||||||
http_proxy = http://user:pwd@192.168.1.128:8080
|
http_proxy = http://user:pwd@192.168.1.128:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 范围端口映射
|
||||||
|
|
||||||
|
在 frpc 的配置文件中可以指定映射多个端口,目前只支持 tcp 和 udp 的类型。
|
||||||
|
|
||||||
|
这一功能通过 `range:` 段落标记来实现,客户端会解析这个标记中的配置,将其拆分成多个 proxy,每一个 proxy 以数字为后缀命名。
|
||||||
|
|
||||||
|
例如要映射本地 6000-6005, 6007 这6个端口,主要配置如下:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# frpc.ini
|
||||||
|
[range:test_tcp]
|
||||||
|
type = tcp
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = 6000-6006,6007
|
||||||
|
remote_port = 6000-6006,6007
|
||||||
|
```
|
||||||
|
|
||||||
|
实际连接成功后会创建 6 个 proxy,命名为 `test_tcp_0, test_tcp_1 ... test_tcp_5`。
|
||||||
|
|
||||||
### 插件
|
### 插件
|
||||||
|
|
||||||
默认情况下,frpc 只会转发请求到本地 tcp 或 udp 端口。
|
默认情况下,frpc 只会转发请求到本地 tcp 或 udp 端口。
|
||||||
|
|
||||||
插件模式是为了在客户端提供更加丰富的功能,目前内置的插件有 **unix_domain_socket**、**http_proxy**、**socks5**。具体使用方式请查看[使用示例](#使用示例)。
|
插件模式是为了在客户端提供更加丰富的功能,目前内置的插件有 `unix_domain_socket`、`http_proxy`、`socks5`、`static_file`。具体使用方式请查看[使用示例](#使用示例)。
|
||||||
|
|
||||||
通过 `plugin` 指定需要使用的插件,插件的配置参数都以 `plugin_` 开头。使用插件后 `local_ip` 和 `local_port` 不再需要配置。
|
通过 `plugin` 指定需要使用的插件,插件的配置参数都以 `plugin_` 开头。使用插件后 `local_ip` 和 `local_port` 不再需要配置。
|
||||||
|
|
||||||
@@ -638,8 +682,6 @@ plugin_http_passwd = abc
|
|||||||
* frps 记录 http 请求日志。
|
* frps 记录 http 请求日志。
|
||||||
* frps 支持直接反向代理,类似 haproxy。
|
* frps 支持直接反向代理,类似 haproxy。
|
||||||
* frpc 支持负载均衡到后端不同服务。
|
* frpc 支持负载均衡到后端不同服务。
|
||||||
* frpc 支持直接作为 webserver 访问指定静态页面。
|
|
||||||
* 支持 udp 打洞的方式,提供两边内网机器直接通信,流量不经过服务器转发。
|
|
||||||
* 集成对 k8s 等平台的支持。
|
* 集成对 k8s 等平台的支持。
|
||||||
|
|
||||||
## 为 frp 做贡献
|
## 为 frp 做贡献
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/g"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
@@ -35,7 +35,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) {
|
|||||||
// url router
|
// url router
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
|
|
||||||
user, passwd := config.ClientCommonCfg.AdminUser, config.ClientCommonCfg.AdminPwd
|
user, passwd := g.GlbClientCfg.AdminUser, g.GlbClientCfg.AdminPwd
|
||||||
|
|
||||||
// api, see dashboard_api.go
|
// api, see dashboard_api.go
|
||||||
router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd))
|
router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd))
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -24,6 +25,7 @@ import (
|
|||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
ini "github.com/vaughan0/go-ini"
|
ini "github.com/vaughan0/go-ini"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
)
|
)
|
||||||
@@ -51,15 +53,16 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
|
|||||||
|
|
||||||
log.Info("Http request: [/api/reload]")
|
log.Info("Http request: [/api/reload]")
|
||||||
|
|
||||||
conf, err := ini.LoadFile(config.ClientCommonCfg.ConfigFile)
|
b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 1
|
res.Code = 1
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Error("reload frpc config file error: %v", err)
|
log.Error("reload frpc config file error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
content := string(b)
|
||||||
|
|
||||||
newCommonCfg, err := config.LoadClientCommonConf(conf)
|
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 2
|
res.Code = 2
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
@@ -67,7 +70,15 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, newCommonCfg.Start)
|
conf, err := ini.LoadFile(g.GlbClientCfg.CfgFile)
|
||||||
|
if err != nil {
|
||||||
|
res.Code = 1
|
||||||
|
res.Msg = err.Error()
|
||||||
|
log.Error("reload frpc config file error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 3
|
res.Code = 3
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
@@ -125,18 +136,18 @@ func NewProxyStatusResp(status *ProxyStatus) ProxyStatusResp {
|
|||||||
}
|
}
|
||||||
psr.Plugin = cfg.Plugin
|
psr.Plugin = cfg.Plugin
|
||||||
if status.Err != "" {
|
if status.Err != "" {
|
||||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
|
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
|
||||||
} else {
|
} else {
|
||||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||||
}
|
}
|
||||||
case *config.UdpProxyConf:
|
case *config.UdpProxyConf:
|
||||||
if cfg.LocalPort != 0 {
|
if cfg.LocalPort != 0 {
|
||||||
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
|
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
|
||||||
}
|
}
|
||||||
if status.Err != "" {
|
if status.Err != "" {
|
||||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
|
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
|
||||||
} else {
|
} else {
|
||||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||||
}
|
}
|
||||||
case *config.HttpProxyConf:
|
case *config.HttpProxyConf:
|
||||||
if cfg.LocalPort != 0 {
|
if cfg.LocalPort != 0 {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/utils/crypto"
|
"github.com/fatedier/frp/utils/crypto"
|
||||||
@@ -29,7 +30,8 @@ import (
|
|||||||
"github.com/fatedier/frp/utils/shutdown"
|
"github.com/fatedier/frp/utils/shutdown"
|
||||||
"github.com/fatedier/frp/utils/util"
|
"github.com/fatedier/frp/utils/util"
|
||||||
"github.com/fatedier/frp/utils/version"
|
"github.com/fatedier/frp/utils/version"
|
||||||
"github.com/xtaci/smux"
|
|
||||||
|
fmux "github.com/hashicorp/yamux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -49,7 +51,7 @@ type Control struct {
|
|||||||
conn frpNet.Conn
|
conn frpNet.Conn
|
||||||
|
|
||||||
// tcp stream multiplexing, if enabled
|
// tcp stream multiplexing, if enabled
|
||||||
session *smux.Session
|
session *fmux.Session
|
||||||
|
|
||||||
// put a message in this channel to send it over control connection to server
|
// put a message in this channel to send it over control connection to server
|
||||||
sendCh chan (msg.Message)
|
sendCh chan (msg.Message)
|
||||||
@@ -82,15 +84,15 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m
|
|||||||
loginMsg := &msg.Login{
|
loginMsg := &msg.Login{
|
||||||
Arch: runtime.GOARCH,
|
Arch: runtime.GOARCH,
|
||||||
Os: runtime.GOOS,
|
Os: runtime.GOOS,
|
||||||
PoolCount: config.ClientCommonCfg.PoolCount,
|
PoolCount: g.GlbClientCfg.PoolCount,
|
||||||
User: config.ClientCommonCfg.User,
|
User: g.GlbClientCfg.User,
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
}
|
}
|
||||||
ctl := &Control{
|
ctl := &Control{
|
||||||
svr: svr,
|
svr: svr,
|
||||||
loginMsg: loginMsg,
|
loginMsg: loginMsg,
|
||||||
sendCh: make(chan msg.Message, 10),
|
sendCh: make(chan msg.Message, 100),
|
||||||
readCh: make(chan msg.Message, 10),
|
readCh: make(chan msg.Message, 100),
|
||||||
closedCh: make(chan int),
|
closedCh: make(chan int),
|
||||||
readerShutdown: shutdown.New(),
|
readerShutdown: shutdown.New(),
|
||||||
writerShutdown: shutdown.New(),
|
writerShutdown: shutdown.New(),
|
||||||
@@ -98,7 +100,7 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m
|
|||||||
Logger: log.NewPrefixLogger(""),
|
Logger: log.NewPrefixLogger(""),
|
||||||
}
|
}
|
||||||
ctl.pm = NewProxyManager(ctl, ctl.sendCh, "")
|
ctl.pm = NewProxyManager(ctl, ctl.sendCh, "")
|
||||||
ctl.pm.Reload(pxyCfgs, visitorCfgs)
|
ctl.pm.Reload(pxyCfgs, visitorCfgs, false)
|
||||||
return ctl
|
return ctl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ func (ctl *Control) Run() (err error) {
|
|||||||
|
|
||||||
// if login_fail_exit is true, just exit this program
|
// if login_fail_exit is true, just exit this program
|
||||||
// otherwise sleep a while and continues relogin to server
|
// otherwise sleep a while and continues relogin to server
|
||||||
if config.ClientCommonCfg.LoginFailExit {
|
if g.GlbClientCfg.LoginFailExit {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
@@ -124,7 +126,7 @@ func (ctl *Control) Run() (err error) {
|
|||||||
|
|
||||||
// start all local visitors and send NewProxy message for all configured proxies
|
// start all local visitors and send NewProxy message for all configured proxies
|
||||||
ctl.pm.Reset(ctl.sendCh, ctl.runId)
|
ctl.pm.Reset(ctl.sendCh, ctl.runId)
|
||||||
ctl.pm.CheckAndStartProxy()
|
ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,8 +185,8 @@ func (ctl *Control) login() (err error) {
|
|||||||
ctl.session.Close()
|
ctl.session.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
conn, err := frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -195,8 +197,8 @@ func (ctl *Control) login() (err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if config.ClientCommonCfg.TcpMux {
|
if g.GlbClientCfg.TcpMux {
|
||||||
session, errRet := smux.Client(conn, nil)
|
session, errRet := fmux.Client(conn, nil)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
return errRet
|
return errRet
|
||||||
}
|
}
|
||||||
@@ -210,7 +212,7 @@ func (ctl *Control) login() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now)
|
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(g.GlbClientCfg.Token, now)
|
||||||
ctl.loginMsg.Timestamp = now
|
ctl.loginMsg.Timestamp = now
|
||||||
ctl.loginMsg.RunId = ctl.runId
|
ctl.loginMsg.RunId = ctl.runId
|
||||||
|
|
||||||
@@ -234,7 +236,7 @@ func (ctl *Control) login() (err error) {
|
|||||||
ctl.conn = conn
|
ctl.conn = conn
|
||||||
// update runId got from server
|
// update runId got from server
|
||||||
ctl.runId = loginRespMsg.RunId
|
ctl.runId = loginRespMsg.RunId
|
||||||
config.ClientCommonCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
||||||
ctl.ClearLogPrefix()
|
ctl.ClearLogPrefix()
|
||||||
ctl.AddLogPrefix(loginRespMsg.RunId)
|
ctl.AddLogPrefix(loginRespMsg.RunId)
|
||||||
ctl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
|
ctl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
|
||||||
@@ -242,7 +244,7 @@ func (ctl *Control) login() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
||||||
if config.ClientCommonCfg.TcpMux {
|
if g.GlbClientCfg.TcpMux {
|
||||||
stream, errRet := ctl.session.OpenStream()
|
stream, errRet := ctl.session.OpenStream()
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
@@ -251,8 +253,8 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
|||||||
}
|
}
|
||||||
conn = frpNet.WrapConn(stream)
|
conn = frpNet.WrapConn(stream)
|
||||||
} else {
|
} else {
|
||||||
conn, err = frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
conn, err = frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctl.Warn("start new connection to server error: %v", err)
|
ctl.Warn("start new connection to server error: %v", err)
|
||||||
return
|
return
|
||||||
@@ -271,7 +273,7 @@ func (ctl *Control) reader() {
|
|||||||
defer ctl.readerShutdown.Done()
|
defer ctl.readerShutdown.Done()
|
||||||
defer close(ctl.closedCh)
|
defer close(ctl.closedCh)
|
||||||
|
|
||||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
|
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbClientCfg.Token))
|
||||||
for {
|
for {
|
||||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -290,7 +292,7 @@ func (ctl *Control) reader() {
|
|||||||
// writer writes messages got from sendCh to frps
|
// writer writes messages got from sendCh to frps
|
||||||
func (ctl *Control) writer() {
|
func (ctl *Control) writer() {
|
||||||
defer ctl.writerShutdown.Done()
|
defer ctl.writerShutdown.Done()
|
||||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
|
encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbClientCfg.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctl.conn.Error("crypto new writer error: %v", err)
|
ctl.conn.Error("crypto new writer error: %v", err)
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
@@ -318,7 +320,7 @@ func (ctl *Control) msgHandler() {
|
|||||||
}()
|
}()
|
||||||
defer ctl.msgHandlerShutdown.Done()
|
defer ctl.msgHandlerShutdown.Done()
|
||||||
|
|
||||||
hbSend := time.NewTicker(time.Duration(config.ClientCommonCfg.HeartBeatInterval) * time.Second)
|
hbSend := time.NewTicker(time.Duration(g.GlbClientCfg.HeartBeatInterval) * time.Second)
|
||||||
defer hbSend.Stop()
|
defer hbSend.Stop()
|
||||||
hbCheck := time.NewTicker(time.Second)
|
hbCheck := time.NewTicker(time.Second)
|
||||||
defer hbCheck.Stop()
|
defer hbCheck.Stop()
|
||||||
@@ -332,7 +334,7 @@ func (ctl *Control) msgHandler() {
|
|||||||
ctl.Debug("send heartbeat to server")
|
ctl.Debug("send heartbeat to server")
|
||||||
ctl.sendCh <- &msg.Ping{}
|
ctl.sendCh <- &msg.Ping{}
|
||||||
case <-hbCheck.C:
|
case <-hbCheck.C:
|
||||||
if time.Since(ctl.lastPong) > time.Duration(config.ClientCommonCfg.HeartBeatTimeout)*time.Second {
|
if time.Since(ctl.lastPong) > time.Duration(g.GlbClientCfg.HeartBeatTimeout)*time.Second {
|
||||||
ctl.Warn("heartbeat timeout")
|
ctl.Warn("heartbeat timeout")
|
||||||
// let reader() stop
|
// let reader() stop
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
@@ -360,20 +362,20 @@ func (ctl *Control) msgHandler() {
|
|||||||
// If controler is notified by closedCh, reader and writer and handler will exit, then recall these functions.
|
// If controler is notified by closedCh, reader and writer and handler will exit, then recall these functions.
|
||||||
func (ctl *Control) worker() {
|
func (ctl *Control) worker() {
|
||||||
go ctl.msgHandler()
|
go ctl.msgHandler()
|
||||||
go ctl.writer()
|
|
||||||
go ctl.reader()
|
go ctl.reader()
|
||||||
|
go ctl.writer()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
maxDelayTime := 20 * time.Second
|
maxDelayTime := 20 * time.Second
|
||||||
delayTime := time.Second
|
delayTime := time.Second
|
||||||
|
|
||||||
checkInterval := 10 * time.Second
|
checkInterval := 60 * time.Second
|
||||||
checkProxyTicker := time.NewTicker(checkInterval)
|
checkProxyTicker := time.NewTicker(checkInterval)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-checkProxyTicker.C:
|
case <-checkProxyTicker.C:
|
||||||
// every 10 seconds, check which proxy registered failed and reregister it to server
|
// check which proxy registered failed and reregister it to server
|
||||||
ctl.pm.CheckAndStartProxy()
|
ctl.pm.CheckAndStartProxy([]string{ProxyStatusStartErr, ProxyStatusClosed})
|
||||||
case _, ok := <-ctl.closedCh:
|
case _, ok := <-ctl.closedCh:
|
||||||
// we won't get any variable from this channel
|
// we won't get any variable from this channel
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -413,8 +415,8 @@ func (ctl *Control) worker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init related channels and variables
|
// init related channels and variables
|
||||||
ctl.sendCh = make(chan msg.Message, 10)
|
ctl.sendCh = make(chan msg.Message, 100)
|
||||||
ctl.readCh = make(chan msg.Message, 10)
|
ctl.readCh = make(chan msg.Message, 100)
|
||||||
ctl.closedCh = make(chan int)
|
ctl.closedCh = make(chan int)
|
||||||
ctl.readerShutdown = shutdown.New()
|
ctl.readerShutdown = shutdown.New()
|
||||||
ctl.writerShutdown = shutdown.New()
|
ctl.writerShutdown = shutdown.New()
|
||||||
@@ -427,7 +429,7 @@ func (ctl *Control) worker() {
|
|||||||
go ctl.reader()
|
go ctl.reader()
|
||||||
|
|
||||||
// start all configured proxies
|
// start all configured proxies
|
||||||
ctl.pm.CheckAndStartProxy()
|
ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew, ProxyStatusClosed})
|
||||||
|
|
||||||
checkProxyTicker.Stop()
|
checkProxyTicker.Stop()
|
||||||
checkProxyTicker = time.NewTicker(checkInterval)
|
checkProxyTicker = time.NewTicker(checkInterval)
|
||||||
@@ -437,6 +439,6 @@ func (ctl *Control) worker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Control) reloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) error {
|
func (ctl *Control) reloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) error {
|
||||||
err := ctl.pm.Reload(pxyCfgs, visitorCfgs)
|
err := ctl.pm.Reload(pxyCfgs, visitorCfgs, true)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/models/plugin"
|
"github.com/fatedier/frp/models/plugin"
|
||||||
@@ -46,7 +47,7 @@ type Proxy interface {
|
|||||||
|
|
||||||
func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
|
func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
|
||||||
baseProxy := BaseProxy{
|
baseProxy := BaseProxy{
|
||||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||||
}
|
}
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
case *config.TcpProxyConf:
|
case *config.TcpProxyConf:
|
||||||
@@ -115,7 +116,7 @@ func (pxy *TcpProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
@@ -144,7 +145,7 @@ func (pxy *HttpProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPS
|
// HTTPS
|
||||||
@@ -173,7 +174,7 @@ func (pxy *HttpsProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// STCP
|
// STCP
|
||||||
@@ -202,7 +203,7 @@ func (pxy *StcpProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
[]byte(g.GlbClientCfg.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// XTCP
|
// XTCP
|
||||||
@@ -243,7 +244,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
|
|||||||
Sid: natHoleSidMsg.Sid,
|
Sid: natHoleSidMsg.Sid,
|
||||||
}
|
}
|
||||||
raddr, _ := net.ResolveUDPAddr("udp",
|
raddr, _ := net.ResolveUDPAddr("udp",
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
|
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
|
||||||
clientConn, err := net.DialUDP("udp", nil, raddr)
|
clientConn, err := net.DialUDP("udp", nil, raddr)
|
||||||
defer clientConn.Close()
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProxyStatusNew = "new"
|
ProxyStatusNew = "new"
|
||||||
ProxyStatusStartErr = "start error"
|
ProxyStatusStartErr = "start error"
|
||||||
ProxyStatusRunning = "running"
|
ProxyStatusWaitStart = "wait start"
|
||||||
ProxyStatusClosed = "closed"
|
ProxyStatusRunning = "running"
|
||||||
|
ProxyStatusClosed = "closed"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProxyManager struct {
|
type ProxyManager struct {
|
||||||
@@ -61,22 +62,18 @@ type ProxyStatus struct {
|
|||||||
|
|
||||||
func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper {
|
func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper {
|
||||||
return &ProxyWrapper{
|
return &ProxyWrapper{
|
||||||
Name: cfg.GetName(),
|
Name: cfg.GetBaseInfo().ProxyName,
|
||||||
Type: cfg.GetType(),
|
Type: cfg.GetBaseInfo().ProxyType,
|
||||||
Status: ProxyStatusNew,
|
Status: ProxyStatusNew,
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
pxy: nil,
|
pxy: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pw *ProxyWrapper) IsRunning() bool {
|
func (pw *ProxyWrapper) GetStatusStr() string {
|
||||||
pw.mu.RLock()
|
pw.mu.RLock()
|
||||||
defer pw.mu.RUnlock()
|
defer pw.mu.RUnlock()
|
||||||
if pw.Status == ProxyStatusRunning {
|
return pw.Status
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pw *ProxyWrapper) GetStatus() *ProxyStatus {
|
func (pw *ProxyWrapper) GetStatus() *ProxyStatus {
|
||||||
@@ -93,6 +90,12 @@ func (pw *ProxyWrapper) GetStatus() *ProxyStatus {
|
|||||||
return ps
|
return ps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pw *ProxyWrapper) WaitStart() {
|
||||||
|
pw.mu.Lock()
|
||||||
|
defer pw.mu.Unlock()
|
||||||
|
pw.Status = ProxyStatusWaitStart
|
||||||
|
}
|
||||||
|
|
||||||
func (pw *ProxyWrapper) Start(remoteAddr string, serverRespErr string) error {
|
func (pw *ProxyWrapper) Start(remoteAddr string, serverRespErr string) error {
|
||||||
if pw.pxy != nil {
|
if pw.pxy != nil {
|
||||||
pw.pxy.Close()
|
pw.pxy.Close()
|
||||||
@@ -210,7 +213,8 @@ func (pm *ProxyManager) CloseProxies() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProxyManager) CheckAndStartProxy() {
|
// pxyStatus: check and start proxies in which status
|
||||||
|
func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
|
||||||
pm.mu.RLock()
|
pm.mu.RLock()
|
||||||
defer pm.mu.RUnlock()
|
defer pm.mu.RUnlock()
|
||||||
if pm.closed {
|
if pm.closed {
|
||||||
@@ -219,35 +223,46 @@ func (pm *ProxyManager) CheckAndStartProxy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, pxy := range pm.proxies {
|
for _, pxy := range pm.proxies {
|
||||||
if !pxy.IsRunning() {
|
status := pxy.GetStatusStr()
|
||||||
var newProxyMsg msg.NewProxy
|
for _, s := range pxyStatus {
|
||||||
pxy.Cfg.UnMarshalToMsg(&newProxyMsg)
|
if status == s {
|
||||||
err := pm.sendMsg(&newProxyMsg)
|
var newProxyMsg msg.NewProxy
|
||||||
if err != nil {
|
pxy.Cfg.MarshalToMsg(&newProxyMsg)
|
||||||
pm.Warn("[%s] proxy send NewProxy message error")
|
err := pm.sendMsg(&newProxyMsg)
|
||||||
return
|
if err != nil {
|
||||||
|
pm.Warn("[%s] proxy send NewProxy message error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pxy.WaitStart()
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cfg := range pm.visitorCfgs {
|
for _, cfg := range pm.visitorCfgs {
|
||||||
if _, exist := pm.visitors[cfg.GetName()]; !exist {
|
name := cfg.GetBaseInfo().ProxyName
|
||||||
pm.Info("try to start visitor [%s]", cfg.GetName())
|
if _, exist := pm.visitors[name]; !exist {
|
||||||
|
pm.Info("try to start visitor [%s]", name)
|
||||||
visitor := NewVisitor(pm.ctl, cfg)
|
visitor := NewVisitor(pm.ctl, cfg)
|
||||||
err := visitor.Run()
|
err := visitor.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
visitor.Warn("start error: %v", err)
|
visitor.Warn("start error: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pm.visitors[cfg.GetName()] = visitor
|
pm.visitors[name] = visitor
|
||||||
visitor.Info("start visitor success")
|
visitor.Info("start visitor success")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) error {
|
func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf, startNow bool) error {
|
||||||
pm.mu.Lock()
|
pm.mu.Lock()
|
||||||
defer pm.mu.Unlock()
|
defer func() {
|
||||||
|
pm.mu.Unlock()
|
||||||
|
if startNow {
|
||||||
|
go pm.CheckAndStartProxy([]string{ProxyStatusNew})
|
||||||
|
}
|
||||||
|
}()
|
||||||
if pm.closed {
|
if pm.closed {
|
||||||
err := fmt.Errorf("Reload error: ProxyManager is closed now")
|
err := fmt.Errorf("Reload error: ProxyManager is closed now")
|
||||||
pm.Warn(err.Error())
|
pm.Warn(err.Error())
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
)
|
)
|
||||||
@@ -41,12 +42,12 @@ func (svr *Service) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ClientCommonCfg.AdminPort != 0 {
|
if g.GlbClientCfg.AdminPort != 0 {
|
||||||
err = svr.RunAdminServer(config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
|
err = svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("run admin server error: %v", err)
|
log.Warn("run admin server error: %v", err)
|
||||||
}
|
}
|
||||||
log.Info("admin server listen on %s:%d", config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
|
log.Info("admin server listen on %s:%d", g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
<-svr.closedCh
|
<-svr.closedCh
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
frpIo "github.com/fatedier/frp/utils/io"
|
frpIo "github.com/fatedier/frp/utils/io"
|
||||||
@@ -45,7 +46,7 @@ type Visitor interface {
|
|||||||
func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) {
|
func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) {
|
||||||
baseVisitor := BaseVisitor{
|
baseVisitor := BaseVisitor{
|
||||||
ctl: ctl,
|
ctl: ctl,
|
||||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||||
}
|
}
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
case *config.StcpProxyConf:
|
case *config.StcpProxyConf:
|
||||||
@@ -193,13 +194,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
|
|||||||
defer userConn.Close()
|
defer userConn.Close()
|
||||||
|
|
||||||
sv.Debug("get a new xtcp user connection")
|
sv.Debug("get a new xtcp user connection")
|
||||||
if config.ClientCommonCfg.ServerUdpPort == 0 {
|
if g.GlbClientCfg.ServerUdpPort == 0 {
|
||||||
sv.Error("xtcp is not supported by server")
|
sv.Error("xtcp is not supported by server")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
raddr, err := net.ResolveUDPAddr("udp",
|
raddr, err := net.ResolveUDPAddr("udp",
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
|
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
|
||||||
visitorConn, err := net.DialUDP("udp", nil, raddr)
|
visitorConn, err := net.DialUDP("udp", nil, raddr)
|
||||||
defer visitorConn.Close()
|
defer visitorConn.Close()
|
||||||
|
|
||||||
|
|||||||
286
cmd/frpc/main.go
286
cmd/frpc/main.go
@@ -15,291 +15,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"github.com/fatedier/frp/cmd/frpc/sub"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
docopt "github.com/docopt/docopt-go"
|
|
||||||
"github.com/rodaine/table"
|
|
||||||
ini "github.com/vaughan0/go-ini"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/client"
|
|
||||||
"github.com/fatedier/frp/models/config"
|
|
||||||
"github.com/fatedier/frp/utils/log"
|
|
||||||
"github.com/fatedier/frp/utils/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
configFile string = "./frpc.ini"
|
|
||||||
)
|
|
||||||
|
|
||||||
var usage string = `frpc is the client of frp
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
frpc [-c config_file] [-L log_file] [--log-level=<log_level>] [--server-addr=<server_addr>]
|
|
||||||
frpc reload [-c config_file]
|
|
||||||
frpc status [-c config_file]
|
|
||||||
frpc -h | --help
|
|
||||||
frpc -v | --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-c config_file set config file
|
|
||||||
-L log_file set output log file, including console
|
|
||||||
--log-level=<log_level> set log level: debug, info, warn, error
|
|
||||||
--server-addr=<server_addr> addr which frps is listening for, example: 0.0.0.0:7000
|
|
||||||
-h --help show this screen
|
|
||||||
-v --version show version
|
|
||||||
`
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
sub.Execute()
|
||||||
confFile := "./frpc.ini"
|
|
||||||
// the configures parsed from file will be replaced by those from command line if exist
|
|
||||||
args, err := docopt.Parse(usage, nil, true, version.Full(), false)
|
|
||||||
|
|
||||||
if args["-c"] != nil {
|
|
||||||
confFile = args["-c"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
conf, err := ini.LoadFile(confFile)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.ClientCommonCfg, err = config.LoadClientCommonConf(conf)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
config.ClientCommonCfg.ConfigFile = confFile
|
|
||||||
|
|
||||||
// check if reload command
|
|
||||||
if args["reload"] != nil {
|
|
||||||
if args["reload"].(bool) {
|
|
||||||
if err = CmdReload(); err != nil {
|
|
||||||
fmt.Printf("frps reload error: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("reload success\n")
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if status command
|
|
||||||
if args["status"] != nil {
|
|
||||||
if args["status"].(bool) {
|
|
||||||
if err = CmdStatus(); err != nil {
|
|
||||||
fmt.Printf("frps get status error: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["-L"] != nil {
|
|
||||||
if args["-L"].(string) == "console" {
|
|
||||||
config.ClientCommonCfg.LogWay = "console"
|
|
||||||
} else {
|
|
||||||
config.ClientCommonCfg.LogWay = "file"
|
|
||||||
config.ClientCommonCfg.LogFile = args["-L"].(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["--log-level"] != nil {
|
|
||||||
config.ClientCommonCfg.LogLevel = args["--log-level"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["--server-addr"] != nil {
|
|
||||||
addr := strings.Split(args["--server-addr"].(string), ":")
|
|
||||||
if len(addr) != 2 {
|
|
||||||
fmt.Println("--server-addr format error: example 0.0.0.0:7000")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
serverPort, err := strconv.ParseInt(addr[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("--server-addr format error, example 0.0.0.0:7000")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
config.ClientCommonCfg.ServerAddr = addr[0]
|
|
||||||
config.ClientCommonCfg.ServerPort = int(serverPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["-v"] != nil {
|
|
||||||
if args["-v"].(bool) {
|
|
||||||
fmt.Println(version.Full())
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, config.ClientCommonCfg.Start)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.InitLog(config.ClientCommonCfg.LogWay, config.ClientCommonCfg.LogFile,
|
|
||||||
config.ClientCommonCfg.LogLevel, config.ClientCommonCfg.LogMaxDays)
|
|
||||||
|
|
||||||
svr := client.NewService(pxyCfgs, visitorCfgs)
|
|
||||||
|
|
||||||
// Capture the exit signal if we use kcp.
|
|
||||||
if config.ClientCommonCfg.Protocol == "kcp" {
|
|
||||||
go HandleSignal(svr)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = svr.Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleSignal(svr *client.Service) {
|
|
||||||
ch := make(chan os.Signal)
|
|
||||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
<-ch
|
|
||||||
svr.Close()
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CmdReload() error {
|
|
||||||
if config.ClientCommonCfg.AdminPort == 0 {
|
|
||||||
return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "http://"+
|
|
||||||
config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/reload", nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
|
|
||||||
config.ClientCommonCfg.AdminPwd))
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", authStr)
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res := &client.GeneralResponse{}
|
|
||||||
err = json.Unmarshal(body, &res)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
|
||||||
} else if res.Code != 0 {
|
|
||||||
return fmt.Errorf(res.Msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CmdStatus() error {
|
|
||||||
if config.ClientCommonCfg.AdminPort == 0 {
|
|
||||||
return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "http://"+
|
|
||||||
config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/status", nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
|
|
||||||
config.ClientCommonCfg.AdminPwd))
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", authStr)
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res := &client.StatusResp{}
|
|
||||||
err = json.Unmarshal(body, &res)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Proxy Status...")
|
|
||||||
if len(res.Tcp) > 0 {
|
|
||||||
fmt.Printf("TCP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.Tcp {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.Udp) > 0 {
|
|
||||||
fmt.Printf("UDP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.Udp {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.Http) > 0 {
|
|
||||||
fmt.Printf("HTTP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.Http {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.Https) > 0 {
|
|
||||||
fmt.Printf("HTTPS")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.Https {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.Stcp) > 0 {
|
|
||||||
fmt.Printf("STCP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.Stcp {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.Xtcp) > 0 {
|
|
||||||
fmt.Printf("XTCP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.Xtcp {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
96
cmd/frpc/sub/http.go
Normal file
96
cmd/frpc/sub/http.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||||
|
httpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||||
|
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||||
|
httpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&locations, "locations", "", "", "locations")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&httpUser, "http_user", "", "admin", "http auth user")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&httpPwd, "http_pwd", "", "admin", "http auth password")
|
||||||
|
httpCmd.PersistentFlags().StringVarP(&hostHeaderRewrite, "host_header_rewrite", "", "", "host header rewrite")
|
||||||
|
httpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
|
httpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(httpCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpCmd = &cobra.Command{
|
||||||
|
Use: "http",
|
||||||
|
Short: "Run frpc with a single http proxy",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.HttpProxyConf{}
|
||||||
|
var prefix string
|
||||||
|
if user != "" {
|
||||||
|
prefix = user + "."
|
||||||
|
}
|
||||||
|
cfg.ProxyName = prefix + proxyName
|
||||||
|
cfg.ProxyType = consts.HttpProxy
|
||||||
|
cfg.LocalIp = localIp
|
||||||
|
cfg.LocalPort = localPort
|
||||||
|
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||||
|
cfg.SubDomain = subDomain
|
||||||
|
cfg.Locations = strings.Split(locations, ",")
|
||||||
|
cfg.HttpUser = httpUser
|
||||||
|
cfg.HttpPwd = httpPwd
|
||||||
|
cfg.HostHeaderRewrite = hostHeaderRewrite
|
||||||
|
cfg.UseEncryption = useEncryption
|
||||||
|
cfg.UseCompression = useCompression
|
||||||
|
|
||||||
|
err = cfg.CheckForCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(proxyConfs, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
88
cmd/frpc/sub/https.go
Normal file
88
cmd/frpc/sub/https.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||||
|
httpsCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||||
|
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||||
|
httpsCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
|
||||||
|
httpsCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
|
||||||
|
httpsCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
|
httpsCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(httpsCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpsCmd = &cobra.Command{
|
||||||
|
Use: "https",
|
||||||
|
Short: "Run frpc with a single https proxy",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.HttpsProxyConf{}
|
||||||
|
var prefix string
|
||||||
|
if user != "" {
|
||||||
|
prefix = user + "."
|
||||||
|
}
|
||||||
|
cfg.ProxyName = prefix + proxyName
|
||||||
|
cfg.ProxyType = consts.HttpsProxy
|
||||||
|
cfg.LocalIp = localIp
|
||||||
|
cfg.LocalPort = localPort
|
||||||
|
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||||
|
cfg.SubDomain = subDomain
|
||||||
|
cfg.UseEncryption = useEncryption
|
||||||
|
cfg.UseCompression = useCompression
|
||||||
|
|
||||||
|
err = cfg.CheckForCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(proxyConfs, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
92
cmd/frpc/sub/reload.go
Normal file
92
cmd/frpc/sub/reload.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/client"
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(reloadCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var reloadCmd = &cobra.Command{
|
||||||
|
Use: "reload",
|
||||||
|
Short: "Hot-Reload frpc configuration",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeIni, cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = reload()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("frpc reload error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("reload success\n")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func reload() error {
|
||||||
|
if g.GlbClientCfg.AdminPort == 0 {
|
||||||
|
return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://"+
|
||||||
|
g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/reload", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+
|
||||||
|
g.GlbClientCfg.AdminPwd))
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", authStr)
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res := &client.GeneralResponse{}
|
||||||
|
err = json.Unmarshal(body, &res)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||||
|
} else if res.Code != 0 {
|
||||||
|
return fmt.Errorf(res.Msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
215
cmd/frpc/sub/root.go
Normal file
215
cmd/frpc/sub/root.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
ini "github.com/vaughan0/go-ini"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/client"
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/utils/log"
|
||||||
|
"github.com/fatedier/frp/utils/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CfgFileTypeIni = iota
|
||||||
|
CfgFileTypeCmd
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cfgFile string
|
||||||
|
showVersion bool
|
||||||
|
|
||||||
|
serverAddr string
|
||||||
|
user string
|
||||||
|
protocol string
|
||||||
|
token string
|
||||||
|
logLevel string
|
||||||
|
logFile string
|
||||||
|
logMaxDays int
|
||||||
|
|
||||||
|
proxyName string
|
||||||
|
localIp string
|
||||||
|
localPort int
|
||||||
|
remotePort int
|
||||||
|
useEncryption bool
|
||||||
|
useCompression bool
|
||||||
|
customDomains string
|
||||||
|
subDomain string
|
||||||
|
httpUser string
|
||||||
|
httpPwd string
|
||||||
|
locations string
|
||||||
|
hostHeaderRewrite string
|
||||||
|
role string
|
||||||
|
sk string
|
||||||
|
serverName string
|
||||||
|
bindAddr string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "./frpc.ini", "config file of frpc")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "frpc",
|
||||||
|
Short: "frpc is the client of frp (https://github.com/fatedier/frp)",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if showVersion {
|
||||||
|
fmt.Println(version.Full())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not show command usage here.
|
||||||
|
err := runClient(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSignal(svr *client.Service) {
|
||||||
|
ch := make(chan os.Signal)
|
||||||
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-ch
|
||||||
|
svr.Close()
|
||||||
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseClientCommonCfg(fileType int, filePath string) (err error) {
|
||||||
|
if fileType == CfgFileTypeIni {
|
||||||
|
err = parseClientCommonCfgFromIni(filePath)
|
||||||
|
} else if fileType == CfgFileTypeCmd {
|
||||||
|
err = parseClientCommonCfgFromCmd()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.GlbClientCfg.CfgFile = cfgFile
|
||||||
|
|
||||||
|
err = g.GlbClientCfg.ClientCommonConf.Check()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseClientCommonCfgFromIni(filePath string) (err error) {
|
||||||
|
b, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
content := string(b)
|
||||||
|
|
||||||
|
cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
g.GlbClientCfg.ClientCommonConf = *cfg
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseClientCommonCfgFromCmd() (err error) {
|
||||||
|
strs := strings.Split(serverAddr, ":")
|
||||||
|
if len(strs) < 2 {
|
||||||
|
err = fmt.Errorf("invalid server_addr")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strs[0] != "" {
|
||||||
|
g.GlbClientCfg.ServerAddr = strs[0]
|
||||||
|
}
|
||||||
|
g.GlbClientCfg.ServerPort, err = strconv.Atoi(strs[1])
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("invalid server_addr")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.GlbClientCfg.User = user
|
||||||
|
g.GlbClientCfg.Protocol = protocol
|
||||||
|
g.GlbClientCfg.Token = token
|
||||||
|
g.GlbClientCfg.LogLevel = logLevel
|
||||||
|
g.GlbClientCfg.LogFile = logFile
|
||||||
|
g.GlbClientCfg.LogMaxDays = int64(logMaxDays)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runClient(cfgFilePath string) (err error) {
|
||||||
|
err = parseClientCommonCfg(CfgFileTypeIni, cfgFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := ini.LoadFile(cfgFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = startService(pxyCfgs, visitorCfgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) (err error) {
|
||||||
|
log.InitLog(g.GlbClientCfg.LogWay, g.GlbClientCfg.LogFile, g.GlbClientCfg.LogLevel, g.GlbClientCfg.LogMaxDays)
|
||||||
|
if g.GlbClientCfg.DnsServer != "" {
|
||||||
|
s := g.GlbClientCfg.DnsServer
|
||||||
|
if !strings.Contains(s, ":") {
|
||||||
|
s += ":53"
|
||||||
|
}
|
||||||
|
// Change default dns server for frpc
|
||||||
|
net.DefaultResolver = &net.Resolver{
|
||||||
|
PreferGo: true,
|
||||||
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
return net.Dial("udp", s)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svr := client.NewService(pxyCfgs, visitorCfgs)
|
||||||
|
|
||||||
|
// Capture the exit signal if we use kcp.
|
||||||
|
if g.GlbClientCfg.Protocol == "kcp" {
|
||||||
|
go handleSignal(svr)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = svr.Run()
|
||||||
|
return
|
||||||
|
}
|
||||||
146
cmd/frpc/sub/status.go
Normal file
146
cmd/frpc/sub/status.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rodaine/table"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/client"
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(statusCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var statusCmd = &cobra.Command{
|
||||||
|
Use: "status",
|
||||||
|
Short: "Overview of all proxies status",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeIni, cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = status()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("frpc get status error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func status() error {
|
||||||
|
if g.GlbClientCfg.AdminPort == 0 {
|
||||||
|
return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://"+
|
||||||
|
g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/status", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+
|
||||||
|
g.GlbClientCfg.AdminPwd))
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", authStr)
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res := &client.StatusResp{}
|
||||||
|
err = json.Unmarshal(body, &res)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Proxy Status...")
|
||||||
|
if len(res.Tcp) > 0 {
|
||||||
|
fmt.Printf("TCP")
|
||||||
|
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||||
|
for _, ps := range res.Tcp {
|
||||||
|
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||||
|
}
|
||||||
|
tbl.Print()
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
if len(res.Udp) > 0 {
|
||||||
|
fmt.Printf("UDP")
|
||||||
|
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||||
|
for _, ps := range res.Udp {
|
||||||
|
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||||
|
}
|
||||||
|
tbl.Print()
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
if len(res.Http) > 0 {
|
||||||
|
fmt.Printf("HTTP")
|
||||||
|
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||||
|
for _, ps := range res.Http {
|
||||||
|
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||||
|
}
|
||||||
|
tbl.Print()
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
if len(res.Https) > 0 {
|
||||||
|
fmt.Printf("HTTPS")
|
||||||
|
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||||
|
for _, ps := range res.Https {
|
||||||
|
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||||
|
}
|
||||||
|
tbl.Print()
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
if len(res.Stcp) > 0 {
|
||||||
|
fmt.Printf("STCP")
|
||||||
|
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||||
|
for _, ps := range res.Stcp {
|
||||||
|
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||||
|
}
|
||||||
|
tbl.Print()
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
if len(res.Xtcp) > 0 {
|
||||||
|
fmt.Printf("XTCP")
|
||||||
|
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||||
|
for _, ps := range res.Xtcp {
|
||||||
|
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||||
|
}
|
||||||
|
tbl.Print()
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
102
cmd/frpc/sub/stcp.go
Normal file
102
cmd/frpc/sub/stcp.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||||
|
stcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||||
|
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||||
|
stcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||||
|
stcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
|
||||||
|
stcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
|
stcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(stcpCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stcpCmd = &cobra.Command{
|
||||||
|
Use: "stcp",
|
||||||
|
Short: "Run frpc with a single stcp proxy",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.StcpProxyConf{}
|
||||||
|
var prefix string
|
||||||
|
if user != "" {
|
||||||
|
prefix = user + "."
|
||||||
|
}
|
||||||
|
cfg.ProxyName = prefix + proxyName
|
||||||
|
cfg.ProxyType = consts.StcpProxy
|
||||||
|
cfg.Role = role
|
||||||
|
cfg.Sk = sk
|
||||||
|
cfg.ServerName = serverName
|
||||||
|
cfg.LocalIp = localIp
|
||||||
|
cfg.LocalPort = localPort
|
||||||
|
cfg.BindAddr = bindAddr
|
||||||
|
cfg.UseEncryption = useEncryption
|
||||||
|
cfg.UseCompression = useCompression
|
||||||
|
|
||||||
|
err = cfg.CheckForCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Role == "server" {
|
||||||
|
proxyConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(proxyConfs, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visitorConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(nil, visitorConfs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
85
cmd/frpc/sub/tcp.go
Normal file
85
cmd/frpc/sub/tcp.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||||
|
tcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||||
|
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||||
|
tcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||||
|
tcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||||
|
tcpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port")
|
||||||
|
tcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
|
tcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(tcpCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcpCmd = &cobra.Command{
|
||||||
|
Use: "tcp",
|
||||||
|
Short: "Run frpc with a single tcp proxy",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.TcpProxyConf{}
|
||||||
|
var prefix string
|
||||||
|
if user != "" {
|
||||||
|
prefix = user + "."
|
||||||
|
}
|
||||||
|
cfg.ProxyName = prefix + proxyName
|
||||||
|
cfg.ProxyType = consts.TcpProxy
|
||||||
|
cfg.LocalIp = localIp
|
||||||
|
cfg.LocalPort = localPort
|
||||||
|
cfg.RemotePort = remotePort
|
||||||
|
cfg.UseEncryption = useEncryption
|
||||||
|
cfg.UseCompression = useCompression
|
||||||
|
|
||||||
|
err = cfg.CheckForCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(proxyConfs, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
85
cmd/frpc/sub/udp.go
Normal file
85
cmd/frpc/sub/udp.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||||
|
udpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||||
|
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||||
|
udpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||||
|
udpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||||
|
udpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port")
|
||||||
|
udpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
|
udpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(udpCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var udpCmd = &cobra.Command{
|
||||||
|
Use: "udp",
|
||||||
|
Short: "Run frpc with a single udp proxy",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.UdpProxyConf{}
|
||||||
|
var prefix string
|
||||||
|
if user != "" {
|
||||||
|
prefix = user + "."
|
||||||
|
}
|
||||||
|
cfg.ProxyName = prefix + proxyName
|
||||||
|
cfg.ProxyType = consts.UdpProxy
|
||||||
|
cfg.LocalIp = localIp
|
||||||
|
cfg.LocalPort = localPort
|
||||||
|
cfg.RemotePort = remotePort
|
||||||
|
cfg.UseEncryption = useEncryption
|
||||||
|
cfg.UseCompression = useCompression
|
||||||
|
|
||||||
|
err = cfg.CheckForCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(proxyConfs, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
102
cmd/frpc/sub/xtcp.go
Normal file
102
cmd/frpc/sub/xtcp.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||||
|
xtcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||||
|
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||||
|
xtcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||||
|
xtcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
|
||||||
|
xtcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
|
xtcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(xtcpCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xtcpCmd = &cobra.Command{
|
||||||
|
Use: "xtcp",
|
||||||
|
Short: "Run frpc with a single xtcp proxy",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.XtcpProxyConf{}
|
||||||
|
var prefix string
|
||||||
|
if user != "" {
|
||||||
|
prefix = user + "."
|
||||||
|
}
|
||||||
|
cfg.ProxyName = prefix + proxyName
|
||||||
|
cfg.ProxyType = consts.XtcpProxy
|
||||||
|
cfg.Role = role
|
||||||
|
cfg.Sk = sk
|
||||||
|
cfg.ServerName = serverName
|
||||||
|
cfg.LocalIp = localIp
|
||||||
|
cfg.LocalPort = localPort
|
||||||
|
cfg.BindAddr = bindAddr
|
||||||
|
cfg.UseEncryption = useEncryption
|
||||||
|
cfg.UseCompression = useCompression
|
||||||
|
|
||||||
|
err = cfg.CheckForCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Role == "server" {
|
||||||
|
proxyConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(proxyConfs, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visitorConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(nil, visitorConfs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
103
cmd/frps/main.go
103
cmd/frps/main.go
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -14,105 +14,6 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
docopt "github.com/docopt/docopt-go"
|
|
||||||
ini "github.com/vaughan0/go-ini"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/config"
|
|
||||||
"github.com/fatedier/frp/server"
|
|
||||||
"github.com/fatedier/frp/utils/log"
|
|
||||||
"github.com/fatedier/frp/utils/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
var usage string = `frps is the server of frp
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
frps [-c config_file] [-L log_file] [--log-level=<log_level>] [--addr=<bind_addr>]
|
|
||||||
frps -h | --help
|
|
||||||
frps -v | --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-c config_file set config file
|
|
||||||
-L log_file set output log file, including console
|
|
||||||
--log-level=<log_level> set log level: debug, info, warn, error
|
|
||||||
--addr=<bind_addr> listen addr for client, example: 0.0.0.0:7000
|
|
||||||
-h --help show this screen
|
|
||||||
-v --version show version
|
|
||||||
`
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
Execute()
|
||||||
confFile := "./frps.ini"
|
|
||||||
// the configures parsed from file will be replaced by those from command line if exist
|
|
||||||
args, err := docopt.Parse(usage, nil, true, version.Full(), false)
|
|
||||||
|
|
||||||
if args["-c"] != nil {
|
|
||||||
confFile = args["-c"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
conf, err := ini.LoadFile(confFile)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
config.ServerCommonCfg, err = config.LoadServerCommonConf(conf)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["-L"] != nil {
|
|
||||||
if args["-L"].(string) == "console" {
|
|
||||||
config.ServerCommonCfg.LogWay = "console"
|
|
||||||
} else {
|
|
||||||
config.ServerCommonCfg.LogWay = "file"
|
|
||||||
config.ServerCommonCfg.LogFile = args["-L"].(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["--log-level"] != nil {
|
|
||||||
config.ServerCommonCfg.LogLevel = args["--log-level"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["--addr"] != nil {
|
|
||||||
addr := strings.Split(args["--addr"].(string), ":")
|
|
||||||
if len(addr) != 2 {
|
|
||||||
fmt.Println("--addr format error: example 0.0.0.0:7000")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
bindPort, err := strconv.ParseInt(addr[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("--addr format error, example 0.0.0.0:7000")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
config.ServerCommonCfg.BindAddr = addr[0]
|
|
||||||
config.ServerCommonCfg.BindPort = int(bindPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["-v"] != nil {
|
|
||||||
if args["-v"].(bool) {
|
|
||||||
fmt.Println(version.Full())
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.InitLog(config.ServerCommonCfg.LogWay, config.ServerCommonCfg.LogFile,
|
|
||||||
config.ServerCommonCfg.LogLevel, config.ServerCommonCfg.LogMaxDays)
|
|
||||||
|
|
||||||
svr, err := server.NewService()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
log.Info("Start frps success")
|
|
||||||
if config.ServerCommonCfg.PrivilegeMode == true {
|
|
||||||
log.Info("PrivilegeMode is enabled, you should pay more attention to security issues")
|
|
||||||
}
|
|
||||||
server.ServerService = svr
|
|
||||||
svr.Run()
|
|
||||||
}
|
}
|
||||||
|
|||||||
194
cmd/frps/root.go
Normal file
194
cmd/frps/root.go
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/server"
|
||||||
|
"github.com/fatedier/frp/utils/log"
|
||||||
|
"github.com/fatedier/frp/utils/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CfgFileTypeIni = iota
|
||||||
|
CfgFileTypeCmd
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cfgFile string
|
||||||
|
showVersion bool
|
||||||
|
|
||||||
|
bindAddr string
|
||||||
|
bindPort int
|
||||||
|
bindUdpPort int
|
||||||
|
kcpBindPort int
|
||||||
|
proxyBindAddr string
|
||||||
|
vhostHttpPort int
|
||||||
|
vhostHttpsPort int
|
||||||
|
dashboardAddr string
|
||||||
|
dashboardPort int
|
||||||
|
dashboardUser string
|
||||||
|
dashboardPwd string
|
||||||
|
assetsDir string
|
||||||
|
logFile string
|
||||||
|
logWay string
|
||||||
|
logLevel string
|
||||||
|
logMaxDays int64
|
||||||
|
token string
|
||||||
|
authTimeout int64
|
||||||
|
subDomainHost string
|
||||||
|
tcpMux bool
|
||||||
|
allowPorts string
|
||||||
|
maxPoolCount int64
|
||||||
|
maxPortsPerClient int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "", "config file of frps")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "0.0.0.0", "bind address")
|
||||||
|
rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port")
|
||||||
|
rootCmd.PersistentFlags().IntVarP(&bindUdpPort, "bind_udp_port", "", 0, "bind udp port")
|
||||||
|
rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address")
|
||||||
|
rootCmd.PersistentFlags().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http port")
|
||||||
|
rootCmd.PersistentFlags().IntVarP(&vhostHttpsPort, "vhost_https_port", "", 0, "vhost https port")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&dashboardAddr, "dashboard_addr", "", "0.0.0.0", "dasboard address")
|
||||||
|
rootCmd.PersistentFlags().IntVarP(&dashboardPort, "dashboard_port", "", 0, "dashboard port")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&dashboardUser, "dashboard_user", "", "admin", "dashboard user")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&dashboardPwd, "dashboard_pwd", "", "admin", "dashboard password")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&logWay, "log_way", "", "console", "log way")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log_max_days")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&token, "token", "", "", "auth token")
|
||||||
|
rootCmd.PersistentFlags().Int64VarP(&authTimeout, "auth_timeout", "", 900, "auth timeout")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host")
|
||||||
|
rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "frps",
|
||||||
|
Short: "frps is the server of frp (https://github.com/fatedier/frp)",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if showVersion {
|
||||||
|
fmt.Println(version.Full())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if cfgFile != "" {
|
||||||
|
err = parseServerCommonCfg(CfgFileTypeIni, cfgFile)
|
||||||
|
} else {
|
||||||
|
err = parseServerCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runServer()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseServerCommonCfg(fileType int, filePath string) (err error) {
|
||||||
|
if fileType == CfgFileTypeIni {
|
||||||
|
err = parseServerCommonCfgFromIni(filePath)
|
||||||
|
} else if fileType == CfgFileTypeCmd {
|
||||||
|
err = parseServerCommonCfgFromCmd()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.GlbServerCfg.CfgFile = filePath
|
||||||
|
|
||||||
|
err = g.GlbServerCfg.ServerCommonConf.Check()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config.InitServerCfg(&g.GlbServerCfg.ServerCommonConf)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseServerCommonCfgFromIni(filePath string) (err error) {
|
||||||
|
b, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
content := string(b)
|
||||||
|
|
||||||
|
cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
g.GlbServerCfg.ServerCommonConf = *cfg
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseServerCommonCfgFromCmd() (err error) {
|
||||||
|
g.GlbServerCfg.BindAddr = bindAddr
|
||||||
|
g.GlbServerCfg.BindPort = bindPort
|
||||||
|
g.GlbServerCfg.BindUdpPort = bindUdpPort
|
||||||
|
g.GlbServerCfg.KcpBindPort = kcpBindPort
|
||||||
|
g.GlbServerCfg.ProxyBindAddr = proxyBindAddr
|
||||||
|
g.GlbServerCfg.VhostHttpPort = vhostHttpPort
|
||||||
|
g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort
|
||||||
|
g.GlbServerCfg.DashboardAddr = dashboardAddr
|
||||||
|
g.GlbServerCfg.DashboardPort = dashboardPort
|
||||||
|
g.GlbServerCfg.DashboardUser = dashboardUser
|
||||||
|
g.GlbServerCfg.DashboardPwd = dashboardPwd
|
||||||
|
g.GlbServerCfg.LogFile = logFile
|
||||||
|
g.GlbServerCfg.LogWay = logWay
|
||||||
|
g.GlbServerCfg.LogLevel = logLevel
|
||||||
|
g.GlbServerCfg.LogMaxDays = logMaxDays
|
||||||
|
g.GlbServerCfg.Token = token
|
||||||
|
g.GlbServerCfg.AuthTimeout = authTimeout
|
||||||
|
g.GlbServerCfg.SubDomainHost = subDomainHost
|
||||||
|
g.GlbServerCfg.MaxPortsPerClient = maxPortsPerClient
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func runServer() (err error) {
|
||||||
|
log.InitLog(g.GlbServerCfg.LogWay, g.GlbServerCfg.LogFile, g.GlbServerCfg.LogLevel,
|
||||||
|
g.GlbServerCfg.LogMaxDays)
|
||||||
|
svr, err := server.NewService()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Start frps success")
|
||||||
|
server.ServerService = svr
|
||||||
|
svr.Run()
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ server_port = 7000
|
|||||||
|
|
||||||
# if you want to connect frps by http proxy, you can set http_proxy here or in global environment variables
|
# if you want to connect frps by http proxy, you can set http_proxy here or in global environment variables
|
||||||
# it only works when protocol is tcp
|
# it only works when protocol is tcp
|
||||||
# http_proxy = http://user:pwd@192.168.1.128:8080
|
# http_proxy = http://user:passwd@192.168.1.128:8080
|
||||||
|
|
||||||
# console or real logFile path like ./frpc.log
|
# console or real logFile path like ./frpc.log
|
||||||
log_file = ./frpc.log
|
log_file = ./frpc.log
|
||||||
@@ -18,13 +18,13 @@ log_level = info
|
|||||||
log_max_days = 3
|
log_max_days = 3
|
||||||
|
|
||||||
# for authentication
|
# for authentication
|
||||||
privilege_token = 12345678
|
token = 12345678
|
||||||
|
|
||||||
# set admin address for control frpc's action by http api such as reload
|
# set admin address for control frpc's action by http api such as reload
|
||||||
admin_addr = 127.0.0.1
|
admin_addr = 127.0.0.1
|
||||||
admin_port = 7400
|
admin_port = 7400
|
||||||
admin_user = admin
|
admin_user = admin
|
||||||
admin_pwd = admin
|
admin_passwd = admin
|
||||||
|
|
||||||
# connections will be established in advance, default value is zero
|
# connections will be established in advance, default value is zero
|
||||||
pool_count = 5
|
pool_count = 5
|
||||||
@@ -43,6 +43,9 @@ login_fail_exit = true
|
|||||||
# now it supports tcp and kcp, default is tcp
|
# now it supports tcp and kcp, default is tcp
|
||||||
protocol = tcp
|
protocol = tcp
|
||||||
|
|
||||||
|
# specify a dns server, so frpc will use this instead of default one
|
||||||
|
dns_server = 8.8.8.8
|
||||||
|
|
||||||
# proxy names you want to start divided by ','
|
# proxy names you want to start divided by ','
|
||||||
# default is empty, means all proxies
|
# default is empty, means all proxies
|
||||||
# start = ssh,dns
|
# start = ssh,dns
|
||||||
@@ -73,6 +76,16 @@ local_port = 22
|
|||||||
# if remote_port is 0, frps will assgin a random port for you
|
# if remote_port is 0, frps will assgin a random port for you
|
||||||
remote_port = 0
|
remote_port = 0
|
||||||
|
|
||||||
|
# if you want to expose multiple ports, add 'range:' prefix to the section name
|
||||||
|
# frpc will generate multiple proxies such as 'tcp_port_6010', 'tcp_port_6011' and so on.
|
||||||
|
[range:tcp_port]
|
||||||
|
type = tcp
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = 6010-6020,6022,6024-6028
|
||||||
|
remote_port = 6010-6020,6022,6024-6028
|
||||||
|
use_encryption = false
|
||||||
|
use_compression = false
|
||||||
|
|
||||||
[dns]
|
[dns]
|
||||||
type = udp
|
type = udp
|
||||||
local_ip = 114.114.114.114
|
local_ip = 114.114.114.114
|
||||||
@@ -81,6 +94,14 @@ remote_port = 6002
|
|||||||
use_encryption = false
|
use_encryption = false
|
||||||
use_compression = false
|
use_compression = false
|
||||||
|
|
||||||
|
[range:udp_port]
|
||||||
|
type = udp
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = 6010-6020
|
||||||
|
remote_port = 6010-6020
|
||||||
|
use_encryption = false
|
||||||
|
use_compression = false
|
||||||
|
|
||||||
# Resolve your domain names to [server_addr] so you can use http://web01.yourdomain.com to browse web01 and http://web02.yourdomain.com to browse web02
|
# Resolve your domain names to [server_addr] so you can use http://web01.yourdomain.com to browse web01 and http://web02.yourdomain.com to browse web02
|
||||||
[web01]
|
[web01]
|
||||||
type = http
|
type = http
|
||||||
@@ -124,6 +145,22 @@ plugin = http_proxy
|
|||||||
plugin_http_user = abc
|
plugin_http_user = abc
|
||||||
plugin_http_passwd = abc
|
plugin_http_passwd = abc
|
||||||
|
|
||||||
|
[plugin_socks5]
|
||||||
|
type = tcp
|
||||||
|
remote_port = 6005
|
||||||
|
plugin = socks5
|
||||||
|
plugin_user = abc
|
||||||
|
plugin_passwd = abc
|
||||||
|
|
||||||
|
[plugin_static_file]
|
||||||
|
type = tcp
|
||||||
|
remote_port = 6006
|
||||||
|
plugin = static_file
|
||||||
|
plugin_local_path = /var/www/blog
|
||||||
|
plugin_strip_prefix = static
|
||||||
|
plugin_http_user = abc
|
||||||
|
plugin_http_passwd = abc
|
||||||
|
|
||||||
[secret_tcp]
|
[secret_tcp]
|
||||||
# If the type is secret tcp, remote_port is useless
|
# If the type is secret tcp, remote_port is useless
|
||||||
# Who want to connect local port should deploy another frpc with stcp proxy and role is visitor
|
# Who want to connect local port should deploy another frpc with stcp proxy and role is visitor
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ vhost_https_port = 443
|
|||||||
dashboard_addr = 0.0.0.0
|
dashboard_addr = 0.0.0.0
|
||||||
dashboard_port = 7500
|
dashboard_port = 7500
|
||||||
|
|
||||||
# dashboard user and pwd for basic auth protect, if not set, both default value is admin
|
# dashboard user and passwd for basic auth protect, if not set, both default value is admin
|
||||||
dashboard_user = admin
|
dashboard_user = admin
|
||||||
dashboard_pwd = admin
|
dashboard_passwd = admin
|
||||||
|
|
||||||
# dashboard assets directory(only for debug mode)
|
# dashboard assets directory(only for debug mode)
|
||||||
# assets_dir = ./static
|
# assets_dir = ./static
|
||||||
@@ -39,19 +39,22 @@ log_level = info
|
|||||||
|
|
||||||
log_max_days = 3
|
log_max_days = 3
|
||||||
|
|
||||||
# privilege mode is the only supported mode since v0.10.0
|
# auth token
|
||||||
privilege_token = 12345678
|
token = 12345678
|
||||||
|
|
||||||
# heartbeat configure, it's not recommended to modify the default value
|
# heartbeat configure, it's not recommended to modify the default value
|
||||||
# the default value of heartbeat_timeout is 90
|
# the default value of heartbeat_timeout is 90
|
||||||
# heartbeat_timeout = 90
|
# heartbeat_timeout = 90
|
||||||
|
|
||||||
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
|
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
|
||||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
|
|
||||||
# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
|
# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
|
||||||
max_pool_count = 5
|
max_pool_count = 5
|
||||||
|
|
||||||
|
# max ports can be used for each client, default value is 0 means no limit
|
||||||
|
max_ports_per_client = 0
|
||||||
|
|
||||||
# authentication_timeout means the timeout interval (seconds) when the frpc connects frps
|
# authentication_timeout means the timeout interval (seconds) when the frpc connects frps
|
||||||
# if authentication_timeout is zero, the time is not verified, default is 900s
|
# if authentication_timeout is zero, the time is not verified, default is 900s
|
||||||
authentication_timeout = 900
|
authentication_timeout = 900
|
||||||
|
|||||||
32
g/g.go
Normal file
32
g/g.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package g
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
GlbClientCfg *ClientCfg
|
||||||
|
GlbServerCfg *ServerCfg
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
GlbClientCfg = &ClientCfg{
|
||||||
|
ClientCommonConf: *config.GetDefaultClientConf(),
|
||||||
|
}
|
||||||
|
GlbServerCfg = &ServerCfg{
|
||||||
|
ServerCommonConf: *config.GetDefaultServerConf(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientCfg struct {
|
||||||
|
config.ClientCommonConf
|
||||||
|
|
||||||
|
CfgFile string
|
||||||
|
ServerUdpPort int // this is configured by login response from frps
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerCfg struct {
|
||||||
|
config.ServerCommonConf
|
||||||
|
|
||||||
|
CfgFile string
|
||||||
|
}
|
||||||
20
glide.lock
generated
20
glide.lock
generated
@@ -1,5 +1,5 @@
|
|||||||
hash: 4095d78a15bf0e7ffdd63331ce75d7199d663cc8710dcd08b9dcd09ba3183eac
|
hash: e2a62cbc49d9da8ff95682f5c0b7731a7047afdd139acddb691c51ea98f726e1
|
||||||
updated: 2018-01-23T14:48:38.764359+08:00
|
updated: 2018-04-25T02:41:38.15698+08:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/armon/go-socks5
|
- name: github.com/armon/go-socks5
|
||||||
version: e75332964ef517daa070d7c38a9466a0d687e0a5
|
version: e75332964ef517daa070d7c38a9466a0d687e0a5
|
||||||
@@ -7,8 +7,6 @@ imports:
|
|||||||
version: 346938d642f2ec3594ed81d874461961cd0faa76
|
version: 346938d642f2ec3594ed81d874461961cd0faa76
|
||||||
subpackages:
|
subpackages:
|
||||||
- spew
|
- spew
|
||||||
- name: github.com/docopt/docopt-go
|
|
||||||
version: 784ddc588536785e7299f7272f39101f7faccc3f
|
|
||||||
- name: github.com/fatedier/beego
|
- name: github.com/fatedier/beego
|
||||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -18,7 +16,11 @@ imports:
|
|||||||
- name: github.com/golang/snappy
|
- name: github.com/golang/snappy
|
||||||
version: 5979233c5d6225d4a8e438cdd0b411888449ddab
|
version: 5979233c5d6225d4a8e438cdd0b411888449ddab
|
||||||
- name: github.com/gorilla/websocket
|
- name: github.com/gorilla/websocket
|
||||||
version: 292fd08b2560ad524ee37396253d71570339a821
|
version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b
|
||||||
|
- name: github.com/hashicorp/yamux
|
||||||
|
version: 2658be15c5f05e76244154714161f17e3e77de2e
|
||||||
|
- name: github.com/inconshreveable/mousetrap
|
||||||
|
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||||
- name: github.com/julienschmidt/httprouter
|
- name: github.com/julienschmidt/httprouter
|
||||||
version: 8a45e95fc75cb77048068a62daed98cc22fdac7c
|
version: 8a45e95fc75cb77048068a62daed98cc22fdac7c
|
||||||
- name: github.com/klauspost/cpuid
|
- name: github.com/klauspost/cpuid
|
||||||
@@ -37,6 +39,10 @@ imports:
|
|||||||
- fs
|
- fs
|
||||||
- name: github.com/rodaine/table
|
- name: github.com/rodaine/table
|
||||||
version: 212a2ad1c462ed4d5b5511ea2b480a573281dbbd
|
version: 212a2ad1c462ed4d5b5511ea2b480a573281dbbd
|
||||||
|
- name: github.com/spf13/cobra
|
||||||
|
version: a1f051bc3eba734da4772d60e2d677f47cf93ef4
|
||||||
|
- name: github.com/spf13/pflag
|
||||||
|
version: 583c0c0531f06d5278b7d917446061adc344b5cd
|
||||||
- name: github.com/stretchr/testify
|
- name: github.com/stretchr/testify
|
||||||
version: 2402e8e7a02fc811447d11f881aa9746cdc57983
|
version: 2402e8e7a02fc811447d11f881aa9746cdc57983
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -53,10 +59,6 @@ imports:
|
|||||||
- sm4
|
- sm4
|
||||||
- name: github.com/vaughan0/go-ini
|
- name: github.com/vaughan0/go-ini
|
||||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||||
- name: github.com/xtaci/kcp-go
|
|
||||||
version: df437e2b8ec365a336200f9d9da53441cf72ed47
|
|
||||||
- name: github.com/xtaci/smux
|
|
||||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
|
||||||
- name: golang.org/x/crypto
|
- name: golang.org/x/crypto
|
||||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||||
subpackages:
|
subpackages:
|
||||||
|
|||||||
10
glide.yaml
10
glide.yaml
@@ -6,8 +6,6 @@ import:
|
|||||||
version: v1.1.0
|
version: v1.1.0
|
||||||
subpackages:
|
subpackages:
|
||||||
- spew
|
- spew
|
||||||
- package: github.com/docopt/docopt-go
|
|
||||||
version: 0.6.2
|
|
||||||
- package: github.com/fatedier/beego
|
- package: github.com/fatedier/beego
|
||||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -48,10 +46,6 @@ import:
|
|||||||
- sm4
|
- sm4
|
||||||
- package: github.com/vaughan0/go-ini
|
- package: github.com/vaughan0/go-ini
|
||||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||||
- package: github.com/xtaci/kcp-go
|
|
||||||
version: v3.17
|
|
||||||
- package: github.com/xtaci/smux
|
|
||||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
|
||||||
- package: golang.org/x/crypto
|
- package: golang.org/x/crypto
|
||||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -74,3 +68,7 @@ import:
|
|||||||
- package: github.com/rodaine/table
|
- package: github.com/rodaine/table
|
||||||
version: v1.0.0
|
version: v1.0.0
|
||||||
- package: github.com/gorilla/websocket
|
- package: github.com/gorilla/websocket
|
||||||
|
version: v1.2.0
|
||||||
|
- package: github.com/hashicorp/yamux
|
||||||
|
- package: github.com/spf13/cobra
|
||||||
|
version: v0.0.2
|
||||||
|
|||||||
@@ -23,46 +23,41 @@ import (
|
|||||||
ini "github.com/vaughan0/go-ini"
|
ini "github.com/vaughan0/go-ini"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ClientCommonCfg *ClientCommonConf
|
|
||||||
|
|
||||||
// client common config
|
// client common config
|
||||||
type ClientCommonConf struct {
|
type ClientCommonConf struct {
|
||||||
ConfigFile string
|
ServerAddr string `json:"server_addr"`
|
||||||
ServerAddr string
|
ServerPort int `json:"server_port"`
|
||||||
ServerPort int
|
HttpProxy string `json:"http_proxy"`
|
||||||
ServerUdpPort int // this is specified by login response message from frps
|
LogFile string `json:"log_file"`
|
||||||
HttpProxy string
|
LogWay string `json:"log_way"`
|
||||||
LogFile string
|
LogLevel string `json:"log_level"`
|
||||||
LogWay string
|
LogMaxDays int64 `json:"log_max_days"`
|
||||||
LogLevel string
|
Token string `json:"token"`
|
||||||
LogMaxDays int64
|
AdminAddr string `json:"admin_addr"`
|
||||||
PrivilegeToken string
|
AdminPort int `json:"admin_port"`
|
||||||
AdminAddr string
|
AdminUser string `json:"admin_user"`
|
||||||
AdminPort int
|
AdminPwd string `json:"admin_pwd"`
|
||||||
AdminUser string
|
PoolCount int `json:"pool_count"`
|
||||||
AdminPwd string
|
TcpMux bool `json:"tcp_mux"`
|
||||||
PoolCount int
|
User string `json:"user"`
|
||||||
TcpMux bool
|
DnsServer string `json:"dns_server"`
|
||||||
User string
|
LoginFailExit bool `json:"login_fail_exit"`
|
||||||
LoginFailExit bool
|
Start map[string]struct{} `json:"start"`
|
||||||
Start map[string]struct{}
|
Protocol string `json:"protocol"`
|
||||||
Protocol string
|
HeartBeatInterval int64 `json:"heartbeat_interval"`
|
||||||
HeartBeatInterval int64
|
HeartBeatTimeout int64 `json:"heartbeat_timeout"`
|
||||||
HeartBeatTimeout int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDeaultClientCommonConf() *ClientCommonConf {
|
func GetDefaultClientConf() *ClientCommonConf {
|
||||||
return &ClientCommonConf{
|
return &ClientCommonConf{
|
||||||
ConfigFile: "./frpc.ini",
|
|
||||||
ServerAddr: "0.0.0.0",
|
ServerAddr: "0.0.0.0",
|
||||||
ServerPort: 7000,
|
ServerPort: 7000,
|
||||||
ServerUdpPort: 0,
|
HttpProxy: os.Getenv("http_proxy"),
|
||||||
HttpProxy: "",
|
|
||||||
LogFile: "console",
|
LogFile: "console",
|
||||||
LogWay: "console",
|
LogWay: "console",
|
||||||
LogLevel: "info",
|
LogLevel: "info",
|
||||||
LogMaxDays: 3,
|
LogMaxDays: 3,
|
||||||
PrivilegeToken: "",
|
Token: "",
|
||||||
AdminAddr: "127.0.0.1",
|
AdminAddr: "127.0.0.1",
|
||||||
AdminPort: 0,
|
AdminPort: 0,
|
||||||
AdminUser: "",
|
AdminUser: "",
|
||||||
@@ -70,6 +65,7 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
|
|||||||
PoolCount: 1,
|
PoolCount: 1,
|
||||||
TcpMux: true,
|
TcpMux: true,
|
||||||
User: "",
|
User: "",
|
||||||
|
DnsServer: "",
|
||||||
LoginFailExit: true,
|
LoginFailExit: true,
|
||||||
Start: make(map[string]struct{}),
|
Start: make(map[string]struct{}),
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
@@ -78,21 +74,28 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (cfg *ClientCommonConf, err error) {
|
||||||
|
cfg = defaultCfg
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = GetDefaultClientConf()
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := ini.Load(strings.NewReader(content))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("parse ini conf file error: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
v int64
|
v int64
|
||||||
)
|
)
|
||||||
cfg = GetDeaultClientCommonConf()
|
if tmpStr, ok = conf.Get("common", "server_addr"); ok {
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "server_addr")
|
|
||||||
if ok {
|
|
||||||
cfg.ServerAddr = tmpStr
|
cfg.ServerAddr = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "server_port")
|
if tmpStr, ok = conf.Get("common", "server_port"); ok {
|
||||||
if ok {
|
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid server_port")
|
err = fmt.Errorf("Parse conf error: invalid server_port")
|
||||||
@@ -101,16 +104,11 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
cfg.ServerPort = int(v)
|
cfg.ServerPort = int(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "http_proxy")
|
if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
|
||||||
if ok {
|
|
||||||
cfg.HttpProxy = tmpStr
|
cfg.HttpProxy = tmpStr
|
||||||
} else {
|
|
||||||
// get http_proxy from env
|
|
||||||
cfg.HttpProxy = os.Getenv("http_proxy")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_file")
|
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogFile = tmpStr
|
cfg.LogFile = tmpStr
|
||||||
if cfg.LogFile == "console" {
|
if cfg.LogFile == "console" {
|
||||||
cfg.LogWay = "console"
|
cfg.LogWay = "console"
|
||||||
@@ -119,30 +117,25 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_level")
|
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogLevel = tmpStr
|
cfg.LogLevel = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||||
cfg.LogMaxDays = v
|
cfg.LogMaxDays = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "privilege_token")
|
if tmpStr, ok = conf.Get("common", "token"); ok {
|
||||||
if ok {
|
cfg.Token = tmpStr
|
||||||
cfg.PrivilegeToken = tmpStr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_addr")
|
if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AdminAddr = tmpStr
|
cfg.AdminAddr = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_port")
|
if tmpStr, ok = conf.Get("common", "admin_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||||
cfg.AdminPort = int(v)
|
cfg.AdminPort = int(v)
|
||||||
} else {
|
} else {
|
||||||
@@ -151,55 +144,48 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_user")
|
if tmpStr, ok = conf.Get("common", "admin_user"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AdminUser = tmpStr
|
cfg.AdminUser = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "admin_pwd")
|
if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AdminPwd = tmpStr
|
cfg.AdminPwd = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "pool_count")
|
if tmpStr, ok = conf.Get("common", "pool_count"); ok {
|
||||||
if ok {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
cfg.PoolCount = 1
|
|
||||||
} else {
|
|
||||||
cfg.PoolCount = int(v)
|
cfg.PoolCount = int(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||||
if ok && tmpStr == "false" {
|
|
||||||
cfg.TcpMux = false
|
cfg.TcpMux = false
|
||||||
} else {
|
} else {
|
||||||
cfg.TcpMux = true
|
cfg.TcpMux = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "user")
|
if tmpStr, ok = conf.Get("common", "user"); ok {
|
||||||
if ok {
|
|
||||||
cfg.User = tmpStr
|
cfg.User = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "start")
|
if tmpStr, ok = conf.Get("common", "dns_server"); ok {
|
||||||
if ok {
|
cfg.DnsServer = tmpStr
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpStr, ok = conf.Get("common", "start"); ok {
|
||||||
proxyNames := strings.Split(tmpStr, ",")
|
proxyNames := strings.Split(tmpStr, ",")
|
||||||
for _, name := range proxyNames {
|
for _, name := range proxyNames {
|
||||||
cfg.Start[strings.TrimSpace(name)] = struct{}{}
|
cfg.Start[strings.TrimSpace(name)] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "login_fail_exit")
|
if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" {
|
||||||
if ok && tmpStr == "false" {
|
|
||||||
cfg.LoginFailExit = false
|
cfg.LoginFailExit = false
|
||||||
} else {
|
} else {
|
||||||
cfg.LoginFailExit = true
|
cfg.LoginFailExit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "protocol")
|
if tmpStr, ok = conf.Get("common", "protocol"); ok {
|
||||||
if ok {
|
|
||||||
// Now it only support tcp and kcp.
|
// Now it only support tcp and kcp.
|
||||||
if tmpStr != "kcp" {
|
if tmpStr != "kcp" {
|
||||||
tmpStr = "tcp"
|
tmpStr = "tcp"
|
||||||
@@ -207,10 +193,8 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
cfg.Protocol = tmpStr
|
cfg.Protocol = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||||
if ok {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@@ -218,17 +202,18 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "heartbeat_interval")
|
if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
|
||||||
if ok {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
cfg.HeartBeatInterval = v
|
cfg.HeartBeatInterval = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ClientCommonConf) Check() (err error) {
|
||||||
if cfg.HeartBeatInterval <= 0 {
|
if cfg.HeartBeatInterval <= 0 {
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -22,11 +22,14 @@ import (
|
|||||||
|
|
||||||
"github.com/fatedier/frp/models/consts"
|
"github.com/fatedier/frp/models/consts"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
|
"github.com/fatedier/frp/utils/util"
|
||||||
|
|
||||||
ini "github.com/vaughan0/go-ini"
|
ini "github.com/vaughan0/go-ini"
|
||||||
)
|
)
|
||||||
|
|
||||||
var proxyConfTypeMap map[string]reflect.Type
|
var (
|
||||||
|
proxyConfTypeMap map[string]reflect.Type
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proxyConfTypeMap = make(map[string]reflect.Type)
|
proxyConfTypeMap = make(map[string]reflect.Type)
|
||||||
@@ -50,17 +53,16 @@ func NewConfByType(proxyType string) ProxyConf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProxyConf interface {
|
type ProxyConf interface {
|
||||||
GetName() string
|
|
||||||
GetType() string
|
|
||||||
GetBaseInfo() *BaseProxyConf
|
GetBaseInfo() *BaseProxyConf
|
||||||
LoadFromMsg(pMsg *msg.NewProxy)
|
UnmarshalFromMsg(pMsg *msg.NewProxy)
|
||||||
LoadFromFile(name string, conf ini.Section) error
|
UnmarshalFromIni(prefix string, name string, conf ini.Section) error
|
||||||
UnMarshalToMsg(pMsg *msg.NewProxy)
|
MarshalToMsg(pMsg *msg.NewProxy)
|
||||||
Check() error
|
CheckForCli() error
|
||||||
|
CheckForSvr() error
|
||||||
Compare(conf ProxyConf) bool
|
Compare(conf ProxyConf) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
func NewProxyConfFromMsg(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
||||||
if pMsg.ProxyType == "" {
|
if pMsg.ProxyType == "" {
|
||||||
pMsg.ProxyType = consts.TcpProxy
|
pMsg.ProxyType = consts.TcpProxy
|
||||||
}
|
}
|
||||||
@@ -70,12 +72,12 @@ func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
|||||||
err = fmt.Errorf("proxy [%s] type [%s] error", pMsg.ProxyName, pMsg.ProxyType)
|
err = fmt.Errorf("proxy [%s] type [%s] error", pMsg.ProxyName, pMsg.ProxyType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg.LoadFromMsg(pMsg)
|
cfg.UnmarshalFromMsg(pMsg)
|
||||||
err = cfg.Check()
|
err = cfg.CheckForSvr()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err error) {
|
func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg ProxyConf, err error) {
|
||||||
proxyType := section["type"]
|
proxyType := section["type"]
|
||||||
if proxyType == "" {
|
if proxyType == "" {
|
||||||
proxyType = consts.TcpProxy
|
proxyType = consts.TcpProxy
|
||||||
@@ -86,7 +88,10 @@ func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err
|
|||||||
err = fmt.Errorf("proxy [%s] type [%s] error", name, proxyType)
|
err = fmt.Errorf("proxy [%s] type [%s] error", name, proxyType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = cfg.LoadFromFile(name, section)
|
if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = cfg.CheckForCli()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,14 +104,6 @@ type BaseProxyConf struct {
|
|||||||
UseCompression bool `json:"use_compression"`
|
UseCompression bool `json:"use_compression"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) GetName() string {
|
|
||||||
return cfg.ProxyName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) GetType() string {
|
|
||||||
return cfg.ProxyType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf {
|
func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf {
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
@@ -121,23 +118,19 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *BaseProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.ProxyName = pMsg.ProxyName
|
cfg.ProxyName = pMsg.ProxyName
|
||||||
cfg.ProxyType = pMsg.ProxyType
|
cfg.ProxyType = pMsg.ProxyType
|
||||||
cfg.UseEncryption = pMsg.UseEncryption
|
cfg.UseEncryption = pMsg.UseEncryption
|
||||||
cfg.UseCompression = pMsg.UseCompression
|
cfg.UseCompression = pMsg.UseCompression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
|
func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) error {
|
||||||
var (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
)
|
)
|
||||||
if ClientCommonCfg.User != "" {
|
cfg.ProxyName = prefix + name
|
||||||
cfg.ProxyName = ClientCommonCfg.User + "." + name
|
|
||||||
} else {
|
|
||||||
cfg.ProxyName = name
|
|
||||||
}
|
|
||||||
cfg.ProxyType = section["type"]
|
cfg.ProxyType = section["type"]
|
||||||
|
|
||||||
tmpStr, ok = section["use_encryption"]
|
tmpStr, ok = section["use_encryption"]
|
||||||
@@ -152,7 +145,7 @@ func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
pMsg.ProxyName = cfg.ProxyName
|
pMsg.ProxyName = cfg.ProxyName
|
||||||
pMsg.ProxyType = cfg.ProxyType
|
pMsg.ProxyType = cfg.ProxyType
|
||||||
pMsg.UseEncryption = cfg.UseEncryption
|
pMsg.UseEncryption = cfg.UseEncryption
|
||||||
@@ -161,24 +154,21 @@ func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
|||||||
|
|
||||||
// Bind info
|
// Bind info
|
||||||
type BindInfoConf struct {
|
type BindInfoConf struct {
|
||||||
BindAddr string `json:"bind_addr"`
|
RemotePort int `json:"remote_port"`
|
||||||
RemotePort int `json:"remote_port"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) compare(cmp *BindInfoConf) bool {
|
func (cfg *BindInfoConf) compare(cmp *BindInfoConf) bool {
|
||||||
if cfg.BindAddr != cmp.BindAddr ||
|
if cfg.RemotePort != cmp.RemotePort {
|
||||||
cfg.RemotePort != cmp.RemotePort {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *BindInfoConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BindAddr = ServerCommonCfg.ProxyBindAddr
|
|
||||||
cfg.RemotePort = pMsg.RemotePort
|
cfg.RemotePort = pMsg.RemotePort
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *BindInfoConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
var (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
@@ -196,14 +186,10 @@ func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *BindInfoConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
pMsg.RemotePort = cfg.RemotePort
|
pMsg.RemotePort = cfg.RemotePort
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BindInfoConf) check() (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Domain info
|
// Domain info
|
||||||
type DomainConf struct {
|
type DomainConf struct {
|
||||||
CustomDomains []string `json:"custom_domains"`
|
CustomDomains []string `json:"custom_domains"`
|
||||||
@@ -218,12 +204,12 @@ func (cfg *DomainConf) compare(cmp *DomainConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DomainConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *DomainConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.CustomDomains = pMsg.CustomDomains
|
cfg.CustomDomains = pMsg.CustomDomains
|
||||||
cfg.SubDomain = pMsg.SubDomain
|
cfg.SubDomain = pMsg.SubDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *DomainConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
var (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
@@ -238,42 +224,60 @@ func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error
|
|||||||
if tmpStr, ok = section["subdomain"]; ok {
|
if tmpStr, ok = section["subdomain"]; ok {
|
||||||
cfg.SubDomain = tmpStr
|
cfg.SubDomain = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
|
|
||||||
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them", name)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DomainConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *DomainConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
pMsg.CustomDomains = cfg.CustomDomains
|
pMsg.CustomDomains = cfg.CustomDomains
|
||||||
pMsg.SubDomain = cfg.SubDomain
|
pMsg.SubDomain = cfg.SubDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DomainConf) check() (err error) {
|
func (cfg *DomainConf) check() (err error) {
|
||||||
|
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
|
||||||
|
err = fmt.Errorf("custom_domains and subdomain should set at least one of them")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *DomainConf) checkForCli() (err error) {
|
||||||
|
if err = cfg.check(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *DomainConf) checkForSvr() (err error) {
|
||||||
|
if err = cfg.check(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, domain := range cfg.CustomDomains {
|
for _, domain := range cfg.CustomDomains {
|
||||||
if ServerCommonCfg.SubDomainHost != "" && len(strings.Split(ServerCommonCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
if subDomainHost != "" && len(strings.Split(subDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
||||||
if strings.Contains(domain, ServerCommonCfg.SubDomainHost) {
|
if strings.Contains(domain, subDomainHost) {
|
||||||
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, ServerCommonCfg.SubDomainHost)
|
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, subDomainHost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.SubDomain != "" {
|
if cfg.SubDomain != "" {
|
||||||
if ServerCommonCfg.SubDomainHost == "" {
|
if subDomainHost == "" {
|
||||||
return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps")
|
return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps")
|
||||||
}
|
}
|
||||||
if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
|
if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
|
||||||
return fmt.Errorf("'.' and '*' is not supported in subdomain")
|
return fmt.Errorf("'.' and '*' is not supported in subdomain")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local service info
|
// Local service info
|
||||||
type LocalSvrConf struct {
|
type LocalSvrConf struct {
|
||||||
LocalIp string `json:"-"`
|
LocalIp string `json:"local_ip"`
|
||||||
LocalPort int `json:"-"`
|
LocalPort int `json:"local_port"`
|
||||||
|
|
||||||
|
Plugin string `json:"plugin"`
|
||||||
|
PluginParams map[string]string `json:"plugin_params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
||||||
@@ -281,30 +285,6 @@ func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
|||||||
cfg.LocalPort != cmp.LocalPort {
|
cfg.LocalPort != cmp.LocalPort {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *LocalSvrConf) LoadFromFile(name string, section ini.Section) (err error) {
|
|
||||||
if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
|
|
||||||
cfg.LocalIp = "127.0.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpStr, ok := section["local_port"]; ok {
|
|
||||||
if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
|
|
||||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginConf struct {
|
|
||||||
Plugin string `json:"-"`
|
|
||||||
PluginParams map[string]string `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *PluginConf) compare(cmp *PluginConf) bool {
|
|
||||||
if cfg.Plugin != cmp.Plugin ||
|
if cfg.Plugin != cmp.Plugin ||
|
||||||
len(cfg.PluginParams) != len(cmp.PluginParams) {
|
len(cfg.PluginParams) != len(cmp.PluginParams) {
|
||||||
return false
|
return false
|
||||||
@@ -318,7 +298,7 @@ func (cfg *PluginConf) compare(cmp *PluginConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
cfg.Plugin = section["plugin"]
|
cfg.Plugin = section["plugin"]
|
||||||
cfg.PluginParams = make(map[string]string)
|
cfg.PluginParams = make(map[string]string)
|
||||||
if cfg.Plugin != "" {
|
if cfg.Plugin != "" {
|
||||||
@@ -329,7 +309,17 @@ func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Parse conf error: proxy [%s] no plugin info found", name)
|
if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
|
||||||
|
cfg.LocalIp = "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpStr, ok := section["local_port"]; ok {
|
||||||
|
if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
|
||||||
|
return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -340,7 +330,6 @@ type TcpProxyConf struct {
|
|||||||
BindInfoConf
|
BindInfoConf
|
||||||
|
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||||
@@ -351,43 +340,38 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
|
!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *TcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TcpProxyConf) Check() (err error) {
|
func (cfg *TcpProxyConf) CheckForCli() error { return nil }
|
||||||
err = cfg.BindInfoConf.check()
|
|
||||||
return
|
func (cfg *TcpProxyConf) CheckForSvr() error { return nil }
|
||||||
}
|
|
||||||
|
|
||||||
// UDP
|
// UDP
|
||||||
type UdpProxyConf struct {
|
type UdpProxyConf struct {
|
||||||
@@ -411,33 +395,32 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *UdpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *UdpProxyConf) Check() (err error) {
|
func (cfg *UdpProxyConf) CheckForCli() error { return nil }
|
||||||
err = cfg.BindInfoConf.check()
|
|
||||||
return
|
func (cfg *UdpProxyConf) CheckForSvr() error { return nil }
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
type HttpProxyConf struct {
|
type HttpProxyConf struct {
|
||||||
@@ -445,12 +428,11 @@ type HttpProxyConf struct {
|
|||||||
DomainConf
|
DomainConf
|
||||||
|
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
|
|
||||||
Locations []string `json:"locations"`
|
Locations []string `json:"locations"`
|
||||||
HostHeaderRewrite string `json:"host_header_rewrite"`
|
HostHeaderRewrite string `json:"host_header_rewrite"`
|
||||||
HttpUser string `json:"-"`
|
HttpUser string `json:"http_user"`
|
||||||
HttpPwd string `json:"-"`
|
HttpPwd string `json:"http_pwd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||||
@@ -462,7 +444,6 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
|
||||||
strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
|
strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
|
||||||
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
|
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
|
||||||
cfg.HttpUser != cmpConf.HttpUser ||
|
cfg.HttpUser != cmpConf.HttpUser ||
|
||||||
@@ -472,9 +453,9 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||||
|
|
||||||
cfg.Locations = pMsg.Locations
|
cfg.Locations = pMsg.Locations
|
||||||
cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
|
cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
|
||||||
@@ -482,17 +463,15 @@ func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
|||||||
cfg.HttpPwd = pMsg.HttpPwd
|
cfg.HttpPwd = pMsg.HttpPwd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -511,9 +490,9 @@ func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||||
|
|
||||||
pMsg.Locations = cfg.Locations
|
pMsg.Locations = cfg.Locations
|
||||||
pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
|
pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
|
||||||
@@ -521,11 +500,20 @@ func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
|||||||
pMsg.HttpPwd = cfg.HttpPwd
|
pMsg.HttpPwd = cfg.HttpPwd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpProxyConf) Check() (err error) {
|
func (cfg *HttpProxyConf) CheckForCli() (err error) {
|
||||||
if ServerCommonCfg.VhostHttpPort == 0 {
|
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||||
return fmt.Errorf("type [http] not support when vhost_http_port is not set")
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *HttpProxyConf) CheckForSvr() (err error) {
|
||||||
|
if vhostHttpPort == 0 {
|
||||||
|
err = fmt.Errorf("type [http] not support when vhost_http_port is not set")
|
||||||
|
}
|
||||||
|
if err = cfg.DomainConf.checkForSvr(); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
err = cfg.DomainConf.check()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,7 +523,6 @@ type HttpsProxyConf struct {
|
|||||||
DomainConf
|
DomainConf
|
||||||
|
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
||||||
@@ -546,43 +533,49 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpsProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *HttpsProxyConf) Check() (err error) {
|
func (cfg *HttpsProxyConf) CheckForCli() (err error) {
|
||||||
if ServerCommonCfg.VhostHttpsPort == 0 {
|
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *HttpsProxyConf) CheckForSvr() (err error) {
|
||||||
|
if vhostHttpsPort == 0 {
|
||||||
return fmt.Errorf("type [https] not support when vhost_https_port is not set")
|
return fmt.Errorf("type [https] not support when vhost_https_port is not set")
|
||||||
}
|
}
|
||||||
err = cfg.DomainConf.check()
|
if err = cfg.DomainConf.checkForSvr(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,7 +588,6 @@ type StcpProxyConf struct {
|
|||||||
|
|
||||||
// used in role server
|
// used in role server
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
|
|
||||||
// used in role visitor
|
// used in role visitor
|
||||||
ServerName string `json:"server_name"`
|
ServerName string `json:"server_name"`
|
||||||
@@ -611,7 +603,6 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
|
||||||
cfg.Role != cmpConf.Role ||
|
cfg.Role != cmpConf.Role ||
|
||||||
cfg.Sk != cmpConf.Sk ||
|
cfg.Sk != cmpConf.Sk ||
|
||||||
cfg.ServerName != cmpConf.ServerName ||
|
cfg.ServerName != cmpConf.ServerName ||
|
||||||
@@ -623,13 +614,13 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only for role server.
|
// Only for role server.
|
||||||
func (cfg *StcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *StcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.Sk = pMsg.Sk
|
cfg.Sk = pMsg.Sk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,21 +651,33 @@ func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
|||||||
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *StcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
pMsg.Sk = cfg.Sk
|
pMsg.Sk = cfg.Sk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *StcpProxyConf) Check() (err error) {
|
func (cfg *StcpProxyConf) CheckForCli() (err error) {
|
||||||
|
if cfg.Role != "server" && cfg.Role != "visitor" {
|
||||||
|
err = fmt.Errorf("role should be 'server' or 'visitor'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cfg.Role == "visitor" {
|
||||||
|
if cfg.BindAddr == "" {
|
||||||
|
err = fmt.Errorf("bind_addr shouldn't be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *StcpProxyConf) CheckForSvr() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,7 +690,6 @@ type XtcpProxyConf struct {
|
|||||||
|
|
||||||
// used in role server
|
// used in role server
|
||||||
LocalSvrConf
|
LocalSvrConf
|
||||||
PluginConf
|
|
||||||
|
|
||||||
// used in role visitor
|
// used in role visitor
|
||||||
ServerName string `json:"server_name"`
|
ServerName string `json:"server_name"`
|
||||||
@@ -703,7 +705,6 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
|
|
||||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
|
||||||
cfg.Role != cmpConf.Role ||
|
cfg.Role != cmpConf.Role ||
|
||||||
cfg.Sk != cmpConf.Sk ||
|
cfg.Sk != cmpConf.Sk ||
|
||||||
cfg.ServerName != cmpConf.ServerName ||
|
cfg.ServerName != cmpConf.ServerName ||
|
||||||
@@ -715,13 +716,13 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only for role server.
|
// Only for role server.
|
||||||
func (cfg *XtcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
func (cfg *XtcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
cfg.Sk = pMsg.Sk
|
cfg.Sk = pMsg.Sk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,27 +753,71 @@ func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
|||||||
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *XtcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
pMsg.Sk = cfg.Sk
|
pMsg.Sk = cfg.Sk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *XtcpProxyConf) Check() (err error) {
|
func (cfg *XtcpProxyConf) CheckForCli() (err error) {
|
||||||
|
if cfg.Role != "server" && cfg.Role != "visitor" {
|
||||||
|
err = fmt.Errorf("role should be 'server' or 'visitor'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cfg.Role == "visitor" {
|
||||||
|
if cfg.BindAddr == "" {
|
||||||
|
err = fmt.Errorf("bind_addr shouldn't be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *XtcpProxyConf) CheckForSvr() (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseRangeSection(name string, section ini.Section) (sections map[string]ini.Section, err error) {
|
||||||
|
localPorts, errRet := util.ParseRangeNumbers(section["local_port"])
|
||||||
|
if errRet != nil {
|
||||||
|
err = fmt.Errorf("Parse conf error: range section [%s] local_port invalid, %v", name, errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
remotePorts, errRet := util.ParseRangeNumbers(section["remote_port"])
|
||||||
|
if errRet != nil {
|
||||||
|
err = fmt.Errorf("Parse conf error: range section [%s] remote_port invalid, %v", name, errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(localPorts) != len(remotePorts) {
|
||||||
|
err = fmt.Errorf("Parse conf error: range section [%s] local ports number should be same with remote ports number", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(localPorts) == 0 {
|
||||||
|
err = fmt.Errorf("Parse conf error: range section [%s] local_port and remote_port is necessary", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sections = make(map[string]ini.Section)
|
||||||
|
for i, port := range localPorts {
|
||||||
|
subName := fmt.Sprintf("%s_%d", name, i)
|
||||||
|
subSection := copySection(section)
|
||||||
|
subSection["local_port"] = fmt.Sprintf("%d", port)
|
||||||
|
subSection["remote_port"] = fmt.Sprintf("%d", remotePorts[i])
|
||||||
|
sections[subName] = subSection
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if len(startProxy) is 0, start all
|
// if len(startProxy) is 0, start all
|
||||||
// otherwise just start proxies in startProxy map
|
// otherwise just start proxies in startProxy map
|
||||||
func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]struct{}) (
|
func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) (
|
||||||
proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) {
|
proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) {
|
||||||
|
|
||||||
if prefix != "" {
|
if prefix != "" {
|
||||||
@@ -786,22 +831,49 @@ func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]s
|
|||||||
proxyConfs = make(map[string]ProxyConf)
|
proxyConfs = make(map[string]ProxyConf)
|
||||||
visitorConfs = make(map[string]ProxyConf)
|
visitorConfs = make(map[string]ProxyConf)
|
||||||
for name, section := range conf {
|
for name, section := range conf {
|
||||||
|
if name == "common" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
_, shouldStart := startProxy[name]
|
_, shouldStart := startProxy[name]
|
||||||
if name != "common" && (startAll || shouldStart) {
|
if !startAll && !shouldStart {
|
||||||
// some proxy or visotr configure may be used this prefix
|
continue
|
||||||
section["prefix"] = prefix
|
}
|
||||||
cfg, err := NewProxyConfFromFile(name, section)
|
|
||||||
|
subSections := make(map[string]ini.Section)
|
||||||
|
|
||||||
|
if strings.HasPrefix(name, "range:") {
|
||||||
|
// range section
|
||||||
|
rangePrefix := strings.TrimSpace(strings.TrimPrefix(name, "range:"))
|
||||||
|
subSections, err = ParseRangeSection(rangePrefix, section)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subSections[name] = section
|
||||||
|
}
|
||||||
|
|
||||||
|
for subName, subSection := range subSections {
|
||||||
|
cfg, err := NewProxyConfFromIni(prefix, subName, subSection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return proxyConfs, visitorConfs, err
|
return proxyConfs, visitorConfs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
role := section["role"]
|
role := subSection["role"]
|
||||||
if role == "visitor" {
|
if role == "visitor" {
|
||||||
visitorConfs[prefix+name] = cfg
|
visitorConfs[prefix+subName] = cfg
|
||||||
} else {
|
} else {
|
||||||
proxyConfs[prefix+name] = cfg
|
proxyConfs[prefix+subName] = cfg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copySection(section ini.Section) (out ini.Section) {
|
||||||
|
out = make(ini.Section)
|
||||||
|
for k, v := range section {
|
||||||
|
out[k] = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,94 +20,113 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
ini "github.com/vaughan0/go-ini"
|
ini "github.com/vaughan0/go-ini"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/utils/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ServerCommonCfg *ServerCommonConf
|
var (
|
||||||
|
// server global configure used for generate proxy conf used in frps
|
||||||
|
proxyBindAddr string
|
||||||
|
subDomainHost string
|
||||||
|
vhostHttpPort int
|
||||||
|
vhostHttpsPort int
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitServerCfg(cfg *ServerCommonConf) {
|
||||||
|
proxyBindAddr = cfg.ProxyBindAddr
|
||||||
|
subDomainHost = cfg.SubDomainHost
|
||||||
|
vhostHttpPort = cfg.VhostHttpPort
|
||||||
|
vhostHttpsPort = cfg.VhostHttpsPort
|
||||||
|
}
|
||||||
|
|
||||||
// common config
|
// common config
|
||||||
type ServerCommonConf struct {
|
type ServerCommonConf struct {
|
||||||
ConfigFile string
|
BindAddr string `json:"bind_addr"`
|
||||||
BindAddr string
|
BindPort int `json:"bind_port"`
|
||||||
BindPort int
|
BindUdpPort int `json:"bind_udp_port"`
|
||||||
BindUdpPort int
|
KcpBindPort int `json:"kcp_bind_port"`
|
||||||
KcpBindPort int
|
ProxyBindAddr string `json:"proxy_bind_addr"`
|
||||||
ProxyBindAddr string
|
|
||||||
|
|
||||||
// If VhostHttpPort equals 0, don't listen a public port for http protocol.
|
// If VhostHttpPort equals 0, don't listen a public port for http protocol.
|
||||||
VhostHttpPort int
|
VhostHttpPort int `json:"vhost_http_port"`
|
||||||
|
|
||||||
// if VhostHttpsPort equals 0, don't listen a public port for https protocol
|
// if VhostHttpsPort equals 0, don't listen a public port for https protocol
|
||||||
VhostHttpsPort int
|
VhostHttpsPort int `json:"vhost_http_port"`
|
||||||
DashboardAddr string
|
DashboardAddr string `json:"dashboard_addr"`
|
||||||
|
|
||||||
// if DashboardPort equals 0, dashboard is not available
|
// if DashboardPort equals 0, dashboard is not available
|
||||||
DashboardPort int
|
DashboardPort int `json:"dashboard_port"`
|
||||||
DashboardUser string
|
DashboardUser string `json:"dashboard_user"`
|
||||||
DashboardPwd string
|
DashboardPwd string `json:"dashboard_pwd"`
|
||||||
AssetsDir string
|
AssetsDir string `json:"asserts_dir"`
|
||||||
LogFile string
|
LogFile string `json:"log_file"`
|
||||||
LogWay string // console or file
|
LogWay string `json:"log_way"` // console or file
|
||||||
LogLevel string
|
LogLevel string `json:"log_level"`
|
||||||
LogMaxDays int64
|
LogMaxDays int64 `json:"log_max_days"`
|
||||||
PrivilegeMode bool
|
Token string `json:"token"`
|
||||||
PrivilegeToken string
|
AuthTimeout int64 `json:"auth_timeout"`
|
||||||
AuthTimeout int64
|
SubDomainHost string `json:"subdomain_host"`
|
||||||
SubDomainHost string
|
TcpMux bool `json:"tcp_mux"`
|
||||||
TcpMux bool
|
|
||||||
|
|
||||||
PrivilegeAllowPorts map[int]struct{}
|
AllowPorts map[int]struct{}
|
||||||
MaxPoolCount int64
|
MaxPoolCount int64 `json:"max_pool_count"`
|
||||||
HeartBeatTimeout int64
|
MaxPortsPerClient int64 `json:"max_ports_per_client"`
|
||||||
UserConnTimeout int64
|
HeartBeatTimeout int64 `json:"heart_beat_timeout"`
|
||||||
|
UserConnTimeout int64 `json:"user_conn_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultServerCommonConf() *ServerCommonConf {
|
func GetDefaultServerConf() *ServerCommonConf {
|
||||||
return &ServerCommonConf{
|
return &ServerCommonConf{
|
||||||
ConfigFile: "./frps.ini",
|
BindAddr: "0.0.0.0",
|
||||||
BindAddr: "0.0.0.0",
|
BindPort: 7000,
|
||||||
BindPort: 7000,
|
BindUdpPort: 0,
|
||||||
BindUdpPort: 0,
|
KcpBindPort: 0,
|
||||||
KcpBindPort: 0,
|
ProxyBindAddr: "0.0.0.0",
|
||||||
ProxyBindAddr: "0.0.0.0",
|
VhostHttpPort: 0,
|
||||||
VhostHttpPort: 0,
|
VhostHttpsPort: 0,
|
||||||
VhostHttpsPort: 0,
|
DashboardAddr: "0.0.0.0",
|
||||||
DashboardAddr: "0.0.0.0",
|
DashboardPort: 0,
|
||||||
DashboardPort: 0,
|
DashboardUser: "admin",
|
||||||
DashboardUser: "admin",
|
DashboardPwd: "admin",
|
||||||
DashboardPwd: "admin",
|
AssetsDir: "",
|
||||||
AssetsDir: "",
|
LogFile: "console",
|
||||||
LogFile: "console",
|
LogWay: "console",
|
||||||
LogWay: "console",
|
LogLevel: "info",
|
||||||
LogLevel: "info",
|
LogMaxDays: 3,
|
||||||
LogMaxDays: 3,
|
Token: "",
|
||||||
PrivilegeMode: true,
|
AuthTimeout: 900,
|
||||||
PrivilegeToken: "",
|
SubDomainHost: "",
|
||||||
AuthTimeout: 900,
|
TcpMux: true,
|
||||||
SubDomainHost: "",
|
AllowPorts: make(map[int]struct{}),
|
||||||
TcpMux: true,
|
MaxPoolCount: 5,
|
||||||
PrivilegeAllowPorts: make(map[int]struct{}),
|
MaxPortsPerClient: 0,
|
||||||
MaxPoolCount: 5,
|
HeartBeatTimeout: 90,
|
||||||
HeartBeatTimeout: 90,
|
UserConnTimeout: 10,
|
||||||
UserConnTimeout: 10,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load server common configure.
|
func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (cfg *ServerCommonConf, err error) {
|
||||||
func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
cfg = defaultCfg
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = GetDefaultServerConf()
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := ini.Load(strings.NewReader(content))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("parse ini conf file error: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tmpStr string
|
tmpStr string
|
||||||
ok bool
|
ok bool
|
||||||
v int64
|
v int64
|
||||||
)
|
)
|
||||||
cfg = GetDefaultServerCommonConf()
|
if tmpStr, ok = conf.Get("common", "bind_addr"); ok {
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "bind_addr")
|
|
||||||
if ok {
|
|
||||||
cfg.BindAddr = tmpStr
|
cfg.BindAddr = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "bind_port")
|
if tmpStr, ok = conf.Get("common", "bind_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid bind_port")
|
err = fmt.Errorf("Parse conf error: invalid bind_port")
|
||||||
return
|
return
|
||||||
@@ -116,8 +135,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "bind_udp_port")
|
if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
|
err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
|
||||||
return
|
return
|
||||||
@@ -126,8 +144,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "kcp_bind_port")
|
if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
|
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
|
||||||
return
|
return
|
||||||
@@ -136,15 +153,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "proxy_bind_addr")
|
if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
|
||||||
if ok {
|
|
||||||
cfg.ProxyBindAddr = tmpStr
|
cfg.ProxyBindAddr = tmpStr
|
||||||
} else {
|
} else {
|
||||||
cfg.ProxyBindAddr = cfg.BindAddr
|
cfg.ProxyBindAddr = cfg.BindAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "vhost_http_port")
|
if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
|
err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
|
||||||
return
|
return
|
||||||
@@ -155,8 +170,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
cfg.VhostHttpPort = 0
|
cfg.VhostHttpPort = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "vhost_https_port")
|
if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
|
err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
|
||||||
return
|
return
|
||||||
@@ -167,15 +181,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
cfg.VhostHttpsPort = 0
|
cfg.VhostHttpsPort = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_addr")
|
if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
|
||||||
if ok {
|
|
||||||
cfg.DashboardAddr = tmpStr
|
cfg.DashboardAddr = tmpStr
|
||||||
} else {
|
} else {
|
||||||
cfg.DashboardAddr = cfg.BindAddr
|
cfg.DashboardAddr = cfg.BindAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_port")
|
if tmpStr, ok = conf.Get("common", "dashboard_port"); ok {
|
||||||
if ok {
|
|
||||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
err = fmt.Errorf("Parse conf error: invalid dashboard_port")
|
err = fmt.Errorf("Parse conf error: invalid dashboard_port")
|
||||||
return
|
return
|
||||||
@@ -186,23 +198,19 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
cfg.DashboardPort = 0
|
cfg.DashboardPort = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_user")
|
if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
|
||||||
if ok {
|
|
||||||
cfg.DashboardUser = tmpStr
|
cfg.DashboardUser = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "dashboard_pwd")
|
if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
|
||||||
if ok {
|
|
||||||
cfg.DashboardPwd = tmpStr
|
cfg.DashboardPwd = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "assets_dir")
|
if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
|
||||||
if ok {
|
|
||||||
cfg.AssetsDir = tmpStr
|
cfg.AssetsDir = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_file")
|
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogFile = tmpStr
|
cfg.LogFile = tmpStr
|
||||||
if cfg.LogFile == "console" {
|
if cfg.LogFile == "console" {
|
||||||
cfg.LogWay = "console"
|
cfg.LogWay = "console"
|
||||||
@@ -211,84 +219,59 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_level")
|
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||||
if ok {
|
|
||||||
cfg.LogLevel = tmpStr
|
cfg.LogLevel = tmpStr
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||||
if ok {
|
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cfg.LogMaxDays = v
|
cfg.LogMaxDays = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "privilege_mode")
|
cfg.Token, _ = conf.Get("common", "token")
|
||||||
if ok {
|
|
||||||
if tmpStr == "true" {
|
if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {
|
||||||
cfg.PrivilegeMode = true
|
// e.g. 1000-2000,2001,2002,3000-4000
|
||||||
|
ports, errRet := util.ParseRangeNumbers(allowPortsStr)
|
||||||
|
if errRet != nil {
|
||||||
|
err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, port := range ports {
|
||||||
|
cfg.AllowPorts[int(port)] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivilegeMode configure
|
if tmpStr, ok = conf.Get("common", "max_pool_count"); ok {
|
||||||
if cfg.PrivilegeMode == true {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
cfg.PrivilegeToken, _ = conf.Get("common", "privilege_token")
|
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||||
|
return
|
||||||
allowPortsStr, ok := conf.Get("common", "privilege_allow_ports")
|
} else {
|
||||||
if ok {
|
if v < 0 {
|
||||||
// e.g. 1000-2000,2001,2002,3000-4000
|
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||||
portRanges := strings.Split(allowPortsStr, ",")
|
return
|
||||||
for _, portRangeStr := range portRanges {
|
|
||||||
// 1000-2000 or 2001
|
|
||||||
portArray := strings.Split(portRangeStr, "-")
|
|
||||||
// length: only 1 or 2 is correct
|
|
||||||
rangeType := len(portArray)
|
|
||||||
if rangeType == 1 {
|
|
||||||
// single port
|
|
||||||
singlePort, errRet := strconv.ParseInt(portArray[0], 10, 64)
|
|
||||||
if errRet != nil {
|
|
||||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.PrivilegeAllowPorts[int(singlePort)] = struct{}{}
|
|
||||||
} else if rangeType == 2 {
|
|
||||||
// range ports
|
|
||||||
min, errRet := strconv.ParseInt(portArray[0], 10, 64)
|
|
||||||
if errRet != nil {
|
|
||||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
max, errRet := strconv.ParseInt(portArray[1], 10, 64)
|
|
||||||
if errRet != nil {
|
|
||||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if max < min {
|
|
||||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports range incorrect")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := min; i <= max; i++ {
|
|
||||||
cfg.PrivilegeAllowPorts[int(i)] = struct{}{}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "max_pool_count")
|
|
||||||
if ok {
|
|
||||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
||||||
if err == nil && v >= 0 {
|
|
||||||
cfg.MaxPoolCount = v
|
cfg.MaxPoolCount = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "authentication_timeout")
|
if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok {
|
||||||
if ok {
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
|
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if v < 0 {
|
||||||
|
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg.MaxPortsPerClient = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpStr, ok = conf.Get("common", "authentication_timeout"); ok {
|
||||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
|
err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
|
||||||
@@ -298,20 +281,17 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "subdomain_host")
|
if tmpStr, ok = conf.Get("common", "subdomain_host"); ok {
|
||||||
if ok {
|
|
||||||
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||||
if ok && tmpStr == "false" {
|
|
||||||
cfg.TcpMux = false
|
cfg.TcpMux = false
|
||||||
} else {
|
} else {
|
||||||
cfg.TcpMux = true
|
cfg.TcpMux = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||||
if ok {
|
|
||||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
||||||
@@ -322,3 +302,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *ServerCommonConf) Check() (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,14 +17,11 @@ package plugin
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/utils/errors"
|
|
||||||
frpIo "github.com/fatedier/frp/utils/io"
|
frpIo "github.com/fatedier/frp/utils/io"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
)
|
)
|
||||||
@@ -35,47 +32,6 @@ func init() {
|
|||||||
Register(PluginHttpProxy, NewHttpProxyPlugin)
|
Register(PluginHttpProxy, NewHttpProxyPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Listener struct {
|
|
||||||
conns chan net.Conn
|
|
||||||
closed bool
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProxyListener() *Listener {
|
|
||||||
return &Listener{
|
|
||||||
conns: make(chan net.Conn, 64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) Accept() (net.Conn, error) {
|
|
||||||
conn, ok := <-l.conns
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("listener closed")
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) PutConn(conn net.Conn) error {
|
|
||||||
err := errors.PanicToError(func() {
|
|
||||||
l.conns <- conn
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) Close() error {
|
|
||||||
l.mu.Lock()
|
|
||||||
defer l.mu.Unlock()
|
|
||||||
if !l.closed {
|
|
||||||
close(l.conns)
|
|
||||||
l.closed = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) Addr() net.Addr {
|
|
||||||
return (*net.TCPAddr)(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
type HttpProxy struct {
|
type HttpProxy struct {
|
||||||
l *Listener
|
l *Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ package plugin
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/utils/errors"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,3 +48,44 @@ type Plugin interface {
|
|||||||
Handle(conn io.ReadWriteCloser, realConn frpNet.Conn)
|
Handle(conn io.ReadWriteCloser, realConn frpNet.Conn)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
conns chan net.Conn
|
||||||
|
closed bool
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyListener() *Listener {
|
||||||
|
return &Listener{
|
||||||
|
conns: make(chan net.Conn, 64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Accept() (net.Conn, error) {
|
||||||
|
conn, ok := <-l.conns
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("listener closed")
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) PutConn(conn net.Conn) error {
|
||||||
|
err := errors.PanicToError(func() {
|
||||||
|
l.conns <- conn
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
if !l.closed {
|
||||||
|
close(l.conns)
|
||||||
|
l.closed = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Addr() net.Addr {
|
||||||
|
return (*net.TCPAddr)(nil)
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,13 +32,23 @@ func init() {
|
|||||||
|
|
||||||
type Socks5Plugin struct {
|
type Socks5Plugin struct {
|
||||||
Server *gosocks5.Server
|
Server *gosocks5.Server
|
||||||
|
|
||||||
|
user string
|
||||||
|
passwd string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
|
func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
|
||||||
sp := &Socks5Plugin{}
|
user := params["plugin_user"]
|
||||||
sp.Server, err = gosocks5.New(&gosocks5.Config{
|
passwd := params["plugin_passwd"]
|
||||||
|
|
||||||
|
cfg := &gosocks5.Config{
|
||||||
Logger: log.New(ioutil.Discard, "", log.LstdFlags),
|
Logger: log.New(ioutil.Discard, "", log.LstdFlags),
|
||||||
})
|
}
|
||||||
|
if user != "" || passwd != "" {
|
||||||
|
cfg.Credentials = gosocks5.StaticCredentials(map[string]string{user: passwd})
|
||||||
|
}
|
||||||
|
sp := &Socks5Plugin{}
|
||||||
|
sp.Server, err = gosocks5.New(cfg)
|
||||||
p = sp
|
p = sp
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
87
models/plugin/static_file.go
Normal file
87
models/plugin/static_file.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PluginStaticFile = "static_file"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(PluginStaticFile, NewStaticFilePlugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
type StaticFilePlugin struct {
|
||||||
|
localPath string
|
||||||
|
stripPrefix string
|
||||||
|
httpUser string
|
||||||
|
httpPasswd string
|
||||||
|
|
||||||
|
l *Listener
|
||||||
|
s *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
|
||||||
|
localPath := params["plugin_local_path"]
|
||||||
|
stripPrefix := params["plugin_strip_prefix"]
|
||||||
|
httpUser := params["plugin_http_user"]
|
||||||
|
httpPasswd := params["plugin_http_passwd"]
|
||||||
|
|
||||||
|
listener := NewProxyListener()
|
||||||
|
|
||||||
|
sp := &StaticFilePlugin{
|
||||||
|
localPath: localPath,
|
||||||
|
stripPrefix: stripPrefix,
|
||||||
|
httpUser: httpUser,
|
||||||
|
httpPasswd: httpPasswd,
|
||||||
|
|
||||||
|
l: listener,
|
||||||
|
}
|
||||||
|
var prefix string
|
||||||
|
if stripPrefix != "" {
|
||||||
|
prefix = "/" + stripPrefix + "/"
|
||||||
|
} else {
|
||||||
|
prefix = "/"
|
||||||
|
}
|
||||||
|
router := httprouter.New()
|
||||||
|
router.Handler("GET", prefix+"*filepath", frpNet.MakeHttpGzipHandler(
|
||||||
|
frpNet.NewHttpBasicAuthWraper(http.StripPrefix(prefix, http.FileServer(http.Dir(localPath))), httpUser, httpPasswd)))
|
||||||
|
sp.s = &http.Server{
|
||||||
|
Handler: router,
|
||||||
|
}
|
||||||
|
go sp.s.Serve(listener)
|
||||||
|
return sp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn) {
|
||||||
|
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
|
||||||
|
sp.l.PutConn(wrapConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *StaticFilePlugin) Name() string {
|
||||||
|
return PluginStaticFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *StaticFilePlugin) Close() error {
|
||||||
|
sp.s.Close()
|
||||||
|
sp.l.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -82,6 +82,7 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<-
|
|||||||
mu.Lock()
|
mu.Lock()
|
||||||
delete(udpConnMap, addr)
|
delete(udpConnMap, addr)
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
udpConn.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
buf := pool.GetBuf(1500)
|
buf := pool.GetBuf(1500)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ make -f ./Makefile.cross-compiles
|
|||||||
rm -rf ./packages
|
rm -rf ./packages
|
||||||
mkdir ./packages
|
mkdir ./packages
|
||||||
|
|
||||||
os_all='linux windows darwin'
|
os_all='linux windows darwin freebsd'
|
||||||
arch_all='386 amd64 arm mips64 mips64le mips mipsle'
|
arch_all='386 amd64 arm mips64 mips64le mips mipsle'
|
||||||
|
|
||||||
for os in $os_all; do
|
for os in $os_all; do
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/consts"
|
"github.com/fatedier/frp/models/consts"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
@@ -55,6 +56,9 @@ type Control struct {
|
|||||||
// pool count
|
// pool count
|
||||||
poolCount int
|
poolCount int
|
||||||
|
|
||||||
|
// ports used, for limitations
|
||||||
|
portsUsedNum int
|
||||||
|
|
||||||
// last time got the Ping message
|
// last time got the Ping message
|
||||||
lastPing time.Time
|
lastPing time.Time
|
||||||
|
|
||||||
@@ -84,6 +88,7 @@ func NewControl(svr *Service, ctlConn net.Conn, loginMsg *msg.Login) *Control {
|
|||||||
workConnCh: make(chan net.Conn, loginMsg.PoolCount+10),
|
workConnCh: make(chan net.Conn, loginMsg.PoolCount+10),
|
||||||
proxies: make(map[string]Proxy),
|
proxies: make(map[string]Proxy),
|
||||||
poolCount: loginMsg.PoolCount,
|
poolCount: loginMsg.PoolCount,
|
||||||
|
portsUsedNum: 0,
|
||||||
lastPing: time.Now(),
|
lastPing: time.Now(),
|
||||||
runId: loginMsg.RunId,
|
runId: loginMsg.RunId,
|
||||||
status: consts.Working,
|
status: consts.Working,
|
||||||
@@ -99,7 +104,7 @@ func (ctl *Control) Start() {
|
|||||||
loginRespMsg := &msg.LoginResp{
|
loginRespMsg := &msg.LoginResp{
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
RunId: ctl.runId,
|
RunId: ctl.runId,
|
||||||
ServerUdpPort: config.ServerCommonCfg.BindUdpPort,
|
ServerUdpPort: g.GlbServerCfg.BindUdpPort,
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
msg.WriteMsg(ctl.conn, loginRespMsg)
|
msg.WriteMsg(ctl.conn, loginRespMsg)
|
||||||
@@ -168,7 +173,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-time.After(time.Duration(config.ServerCommonCfg.UserConnTimeout) * time.Second):
|
case <-time.After(time.Duration(g.GlbServerCfg.UserConnTimeout) * time.Second):
|
||||||
err = fmt.Errorf("timeout trying to get work connection")
|
err = fmt.Errorf("timeout trying to get work connection")
|
||||||
ctl.conn.Warn("%v", err)
|
ctl.conn.Warn("%v", err)
|
||||||
return
|
return
|
||||||
@@ -198,7 +203,7 @@ func (ctl *Control) writer() {
|
|||||||
defer ctl.allShutdown.Start()
|
defer ctl.allShutdown.Start()
|
||||||
defer ctl.writerShutdown.Done()
|
defer ctl.writerShutdown.Done()
|
||||||
|
|
||||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
|
encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbServerCfg.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctl.conn.Error("crypto new writer error: %v", err)
|
ctl.conn.Error("crypto new writer error: %v", err)
|
||||||
ctl.allShutdown.Start()
|
ctl.allShutdown.Start()
|
||||||
@@ -227,7 +232,7 @@ func (ctl *Control) reader() {
|
|||||||
defer ctl.allShutdown.Start()
|
defer ctl.allShutdown.Start()
|
||||||
defer ctl.readerShutdown.Done()
|
defer ctl.readerShutdown.Done()
|
||||||
|
|
||||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
|
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbServerCfg.Token))
|
||||||
for {
|
for {
|
||||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -261,13 +266,14 @@ func (ctl *Control) stoper() {
|
|||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
ctl.readerShutdown.WaitDone()
|
ctl.readerShutdown.WaitDone()
|
||||||
|
|
||||||
|
ctl.mu.Lock()
|
||||||
|
defer ctl.mu.Unlock()
|
||||||
|
|
||||||
close(ctl.workConnCh)
|
close(ctl.workConnCh)
|
||||||
for workConn := range ctl.workConnCh {
|
for workConn := range ctl.workConnCh {
|
||||||
workConn.Close()
|
workConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctl.mu.Lock()
|
|
||||||
defer ctl.mu.Unlock()
|
|
||||||
for _, pxy := range ctl.proxies {
|
for _, pxy := range ctl.proxies {
|
||||||
pxy.Close()
|
pxy.Close()
|
||||||
ctl.svr.DelProxy(pxy.GetName())
|
ctl.svr.DelProxy(pxy.GetName())
|
||||||
@@ -296,9 +302,10 @@ func (ctl *Control) manager() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-heartbeat.C:
|
case <-heartbeat.C:
|
||||||
if time.Since(ctl.lastPing) > time.Duration(config.ServerCommonCfg.HeartBeatTimeout)*time.Second {
|
if time.Since(ctl.lastPing) > time.Duration(g.GlbServerCfg.HeartBeatTimeout)*time.Second {
|
||||||
ctl.conn.Warn("heartbeat timeout")
|
ctl.conn.Warn("heartbeat timeout")
|
||||||
ctl.allShutdown.Start()
|
ctl.allShutdown.Start()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
case rawMsg, ok := <-ctl.readCh:
|
case rawMsg, ok := <-ctl.readCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -336,7 +343,7 @@ func (ctl *Control) manager() {
|
|||||||
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
||||||
var pxyConf config.ProxyConf
|
var pxyConf config.ProxyConf
|
||||||
// Load configures from NewProxy message and check.
|
// Load configures from NewProxy message and check.
|
||||||
pxyConf, err = config.NewProxyConf(pxyMsg)
|
pxyConf, err = config.NewProxyConfFromMsg(pxyMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -348,6 +355,26 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
|
|||||||
return remoteAddr, err
|
return remoteAddr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ports used number in each client
|
||||||
|
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||||
|
ctl.mu.Lock()
|
||||||
|
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(g.GlbServerCfg.MaxPortsPerClient) {
|
||||||
|
ctl.mu.Unlock()
|
||||||
|
err = fmt.Errorf("exceed the max_ports_per_client")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctl.portsUsedNum = ctl.portsUsedNum + pxy.GetUsedPortsNum()
|
||||||
|
ctl.mu.Unlock()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
ctl.mu.Lock()
|
||||||
|
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
|
||||||
|
ctl.mu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
remoteAddr, err = pxy.Run()
|
remoteAddr, err = pxy.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -371,16 +398,21 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
|
|||||||
|
|
||||||
func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
|
func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
|
||||||
ctl.mu.Lock()
|
ctl.mu.Lock()
|
||||||
defer ctl.mu.Unlock()
|
|
||||||
|
|
||||||
pxy, ok := ctl.proxies[closeMsg.ProxyName]
|
pxy, ok := ctl.proxies[closeMsg.ProxyName]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
ctl.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||||
|
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
|
||||||
|
}
|
||||||
pxy.Close()
|
pxy.Close()
|
||||||
ctl.svr.DelProxy(pxy.GetName())
|
ctl.svr.DelProxy(pxy.GetName())
|
||||||
delete(ctl.proxies, closeMsg.ProxyName)
|
delete(ctl.proxies, closeMsg.ProxyName)
|
||||||
|
ctl.mu.Unlock()
|
||||||
|
|
||||||
StatsCloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
|
StatsCloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/assets"
|
"github.com/fatedier/frp/assets"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/g"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
@@ -36,10 +36,14 @@ func RunDashboardServer(addr string, port int) (err error) {
|
|||||||
// url router
|
// url router
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
|
|
||||||
user, passwd := config.ServerCommonCfg.DashboardUser, config.ServerCommonCfg.DashboardPwd
|
user, passwd := g.GlbServerCfg.DashboardUser, g.GlbServerCfg.DashboardPwd
|
||||||
|
|
||||||
// api, see dashboard_api.go
|
// api, see dashboard_api.go
|
||||||
router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd))
|
router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd))
|
||||||
|
router.GET("/api/proxy/tcp/:name", frpNet.HttprouterBasicAuth(apiProxyTcpByName, user, passwd))
|
||||||
|
router.GET("/api/proxy/udp/:name", frpNet.HttprouterBasicAuth(apiProxyUdpByName, user, passwd))
|
||||||
|
router.GET("/api/proxy/http/:name", frpNet.HttprouterBasicAuth(apiProxyHttpByName, user, passwd))
|
||||||
|
router.GET("/api/proxy/https/:name", frpNet.HttprouterBasicAuth(apiProxyHttpsByName, user, passwd))
|
||||||
router.GET("/api/proxy/tcp", frpNet.HttprouterBasicAuth(apiProxyTcp, user, passwd))
|
router.GET("/api/proxy/tcp", frpNet.HttprouterBasicAuth(apiProxyTcp, user, passwd))
|
||||||
router.GET("/api/proxy/udp", frpNet.HttprouterBasicAuth(apiProxyUdp, user, passwd))
|
router.GET("/api/proxy/udp", frpNet.HttprouterBasicAuth(apiProxyUdp, user, passwd))
|
||||||
router.GET("/api/proxy/http", frpNet.HttprouterBasicAuth(apiProxyHttp, user, passwd))
|
router.GET("/api/proxy/http", frpNet.HttprouterBasicAuth(apiProxyHttp, user, passwd))
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/consts"
|
"github.com/fatedier/frp/models/consts"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
@@ -60,7 +61,7 @@ func apiServerInfo(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
log.Info("Http request: [/api/serverinfo]")
|
log.Info("Http request: [/api/serverinfo]")
|
||||||
cfg := config.ServerCommonCfg
|
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||||
serverStats := StatsGetServer()
|
serverStats := StatsGetServer()
|
||||||
res = ServerInfoResp{
|
res = ServerInfoResp{
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
@@ -189,6 +190,119 @@ func getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get proxy info by name.
|
||||||
|
type GetProxyStatsResp struct {
|
||||||
|
GeneralResponse
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Conf config.ProxyConf `json:"conf"`
|
||||||
|
TodayTrafficIn int64 `json:"today_traffic_in"`
|
||||||
|
TodayTrafficOut int64 `json:"today_traffic_out"`
|
||||||
|
CurConns int64 `json:"cur_conns"`
|
||||||
|
LastStartTime string `json:"last_start_time"`
|
||||||
|
LastCloseTime string `json:"last_close_time"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// api/proxy/tcp/:name
|
||||||
|
func apiProxyTcpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
res GetProxyStatsResp
|
||||||
|
)
|
||||||
|
name := params.ByName("name")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
log.Info("Http response [/api/proxy/tcp/:name]: code [%d]", res.Code)
|
||||||
|
}()
|
||||||
|
log.Info("Http request: [/api/proxy/tcp/:name]")
|
||||||
|
|
||||||
|
res = getProxyStatsByTypeAndName(consts.TcpProxy, name)
|
||||||
|
|
||||||
|
buf, _ = json.Marshal(&res)
|
||||||
|
w.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// api/proxy/udp/:name
|
||||||
|
func apiProxyUdpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
res GetProxyStatsResp
|
||||||
|
)
|
||||||
|
name := params.ByName("name")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
log.Info("Http response [/api/proxy/udp/:name]: code [%d]", res.Code)
|
||||||
|
}()
|
||||||
|
log.Info("Http request: [/api/proxy/udp/:name]")
|
||||||
|
|
||||||
|
res = getProxyStatsByTypeAndName(consts.UdpProxy, name)
|
||||||
|
|
||||||
|
buf, _ = json.Marshal(&res)
|
||||||
|
w.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// api/proxy/http/:name
|
||||||
|
func apiProxyHttpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
res GetProxyStatsResp
|
||||||
|
)
|
||||||
|
name := params.ByName("name")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
log.Info("Http response [/api/proxy/http/:name]: code [%d]", res.Code)
|
||||||
|
}()
|
||||||
|
log.Info("Http request: [/api/proxy/http/:name]")
|
||||||
|
|
||||||
|
res = getProxyStatsByTypeAndName(consts.HttpProxy, name)
|
||||||
|
|
||||||
|
buf, _ = json.Marshal(&res)
|
||||||
|
w.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// api/proxy/https/:name
|
||||||
|
func apiProxyHttpsByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
res GetProxyStatsResp
|
||||||
|
)
|
||||||
|
name := params.ByName("name")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
log.Info("Http response [/api/proxy/https/:name]: code [%d]", res.Code)
|
||||||
|
}()
|
||||||
|
log.Info("Http request: [/api/proxy/https/:name]")
|
||||||
|
|
||||||
|
res = getProxyStatsByTypeAndName(consts.HttpsProxy, name)
|
||||||
|
|
||||||
|
buf, _ = json.Marshal(&res)
|
||||||
|
w.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp) {
|
||||||
|
proxyInfo.Name = proxyName
|
||||||
|
ps := StatsGetProxiesByTypeAndName(proxyType, proxyName)
|
||||||
|
if ps == nil {
|
||||||
|
proxyInfo.Code = 1
|
||||||
|
proxyInfo.Msg = "no proxy info found"
|
||||||
|
} else {
|
||||||
|
if pxy, ok := ServerService.pxyManager.GetByName(proxyName); ok {
|
||||||
|
proxyInfo.Conf = pxy.GetConf()
|
||||||
|
proxyInfo.Status = consts.Online
|
||||||
|
} else {
|
||||||
|
proxyInfo.Status = consts.Offline
|
||||||
|
}
|
||||||
|
proxyInfo.TodayTrafficIn = ps.TodayTrafficIn
|
||||||
|
proxyInfo.TodayTrafficOut = ps.TodayTrafficOut
|
||||||
|
proxyInfo.CurConns = ps.CurConns
|
||||||
|
proxyInfo.LastStartTime = ps.LastStartTime
|
||||||
|
proxyInfo.LastCloseTime = ps.LastCloseTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// api/proxy/traffic/:name
|
// api/proxy/traffic/:name
|
||||||
type GetProxyTrafficResp struct {
|
type GetProxyTrafficResp struct {
|
||||||
GeneralResponse
|
GeneralResponse
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
"github.com/fatedier/frp/utils/metric"
|
"github.com/fatedier/frp/utils/metric"
|
||||||
)
|
)
|
||||||
@@ -92,19 +92,19 @@ func StatsClearUselessInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsNewClient() {
|
func StatsNewClient() {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.ClientCounts.Inc(1)
|
globalStats.ClientCounts.Inc(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StatsCloseClient() {
|
func StatsCloseClient() {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.ClientCounts.Dec(1)
|
globalStats.ClientCounts.Dec(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StatsNewProxy(name string, proxyType string) {
|
func StatsNewProxy(name string, proxyType string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
defer globalStats.mu.Unlock()
|
defer globalStats.mu.Unlock()
|
||||||
counter, ok := globalStats.ProxyTypeCounts[proxyType]
|
counter, ok := globalStats.ProxyTypeCounts[proxyType]
|
||||||
@@ -130,7 +130,7 @@ func StatsNewProxy(name string, proxyType string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsCloseProxy(proxyName string, proxyType string) {
|
func StatsCloseProxy(proxyName string, proxyType string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
defer globalStats.mu.Unlock()
|
defer globalStats.mu.Unlock()
|
||||||
if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok {
|
if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok {
|
||||||
@@ -143,7 +143,7 @@ func StatsCloseProxy(proxyName string, proxyType string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsOpenConnection(name string) {
|
func StatsOpenConnection(name string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.CurConns.Inc(1)
|
globalStats.CurConns.Inc(1)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -157,7 +157,7 @@ func StatsOpenConnection(name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsCloseConnection(name string) {
|
func StatsCloseConnection(name string) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.CurConns.Dec(1)
|
globalStats.CurConns.Dec(1)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -171,7 +171,7 @@ func StatsCloseConnection(name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsAddTrafficIn(name string, trafficIn int64) {
|
func StatsAddTrafficIn(name string, trafficIn int64) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.TotalTrafficIn.Inc(trafficIn)
|
globalStats.TotalTrafficIn.Inc(trafficIn)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -186,7 +186,7 @@ func StatsAddTrafficIn(name string, trafficIn int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StatsAddTrafficOut(name string, trafficOut int64) {
|
func StatsAddTrafficOut(name string, trafficOut int64) {
|
||||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
if g.GlbServerCfg.DashboardPort != 0 {
|
||||||
globalStats.TotalTrafficOut.Inc(trafficOut)
|
globalStats.TotalTrafficOut.Inc(trafficOut)
|
||||||
|
|
||||||
globalStats.mu.Lock()
|
globalStats.mu.Lock()
|
||||||
@@ -263,6 +263,37 @@ func StatsGetProxiesByType(proxyType string) []*ProxyStats {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StatsGetProxiesByTypeAndName(proxyType string, proxyName string) (res *ProxyStats) {
|
||||||
|
globalStats.mu.Lock()
|
||||||
|
defer globalStats.mu.Unlock()
|
||||||
|
|
||||||
|
for name, proxyStats := range globalStats.ProxyStatistics {
|
||||||
|
if proxyStats.ProxyType != proxyType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != proxyName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
res = &ProxyStats{
|
||||||
|
Name: name,
|
||||||
|
Type: proxyStats.ProxyType,
|
||||||
|
TodayTrafficIn: proxyStats.TrafficIn.TodayCount(),
|
||||||
|
TodayTrafficOut: proxyStats.TrafficOut.TodayCount(),
|
||||||
|
CurConns: proxyStats.CurConns.Count(),
|
||||||
|
}
|
||||||
|
if !proxyStats.LastStartTime.IsZero() {
|
||||||
|
res.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05")
|
||||||
|
}
|
||||||
|
if !proxyStats.LastCloseTime.IsZero() {
|
||||||
|
res.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type ProxyTrafficInfo struct {
|
type ProxyTrafficInfo struct {
|
||||||
Name string
|
Name string
|
||||||
TrafficIn []int64
|
TrafficIn []int64
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/models/proto/udp"
|
"github.com/fatedier/frp/models/proto/udp"
|
||||||
@@ -40,15 +41,18 @@ type Proxy interface {
|
|||||||
GetName() string
|
GetName() string
|
||||||
GetConf() config.ProxyConf
|
GetConf() config.ProxyConf
|
||||||
GetWorkConnFromPool() (workConn frpNet.Conn, err error)
|
GetWorkConnFromPool() (workConn frpNet.Conn, err error)
|
||||||
|
GetUsedPortsNum() int
|
||||||
Close()
|
Close()
|
||||||
log.Logger
|
log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseProxy struct {
|
type BaseProxy struct {
|
||||||
name string
|
name string
|
||||||
ctl *Control
|
ctl *Control
|
||||||
listeners []frpNet.Listener
|
listeners []frpNet.Listener
|
||||||
mu sync.RWMutex
|
usedPortsNum int
|
||||||
|
|
||||||
|
mu sync.RWMutex
|
||||||
log.Logger
|
log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +64,10 @@ func (pxy *BaseProxy) GetControl() *Control {
|
|||||||
return pxy.ctl
|
return pxy.ctl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pxy *BaseProxy) GetUsedPortsNum() int {
|
||||||
|
return pxy.usedPortsNum
|
||||||
|
}
|
||||||
|
|
||||||
func (pxy *BaseProxy) Close() {
|
func (pxy *BaseProxy) Close() {
|
||||||
pxy.Info("proxy closing")
|
pxy.Info("proxy closing")
|
||||||
for _, l := range pxy.listeners {
|
for _, l := range pxy.listeners {
|
||||||
@@ -119,13 +127,14 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Con
|
|||||||
|
|
||||||
func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
||||||
basePxy := BaseProxy{
|
basePxy := BaseProxy{
|
||||||
name: pxyConf.GetName(),
|
name: pxyConf.GetBaseInfo().ProxyName,
|
||||||
ctl: ctl,
|
ctl: ctl,
|
||||||
listeners: make([]frpNet.Listener, 0),
|
listeners: make([]frpNet.Listener, 0),
|
||||||
Logger: log.NewPrefixLogger(ctl.runId),
|
Logger: log.NewPrefixLogger(ctl.runId),
|
||||||
}
|
}
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
case *config.TcpProxyConf:
|
case *config.TcpProxyConf:
|
||||||
|
basePxy.usedPortsNum = 1
|
||||||
pxy = &TcpProxy{
|
pxy = &TcpProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
@@ -141,6 +150,7 @@ func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.UdpProxyConf:
|
case *config.UdpProxyConf:
|
||||||
|
basePxy.usedPortsNum = 1
|
||||||
pxy = &UdpProxy{
|
pxy = &UdpProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
@@ -182,7 +192,7 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
|
|||||||
|
|
||||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||||
pxy.cfg.RemotePort = pxy.realPort
|
pxy.cfg.RemotePort = pxy.realPort
|
||||||
listener, errRet := frpNet.ListenTcp(config.ServerCommonCfg.ProxyBindAddr, pxy.realPort)
|
listener, errRet := frpNet.ListenTcp(g.GlbServerCfg.ProxyBindAddr, pxy.realPort)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
@@ -235,7 +245,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
|||||||
}
|
}
|
||||||
tmpDomain := routeConfig.Domain
|
tmpDomain := routeConfig.Domain
|
||||||
tmpLocation := routeConfig.Location
|
tmpLocation := routeConfig.Location
|
||||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
|
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(g.GlbServerCfg.VhostHttpPort)))
|
||||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
||||||
})
|
})
|
||||||
@@ -244,7 +254,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pxy.cfg.SubDomain != "" {
|
if pxy.cfg.SubDomain != "" {
|
||||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
|
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
routeConfig.Location = location
|
routeConfig.Location = location
|
||||||
err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig)
|
err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig)
|
||||||
@@ -253,7 +263,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
|||||||
}
|
}
|
||||||
tmpDomain := routeConfig.Domain
|
tmpDomain := routeConfig.Domain
|
||||||
tmpLocation := routeConfig.Location
|
tmpLocation := routeConfig.Location
|
||||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
|
addrs = append(addrs, util.CanonicalAddr(tmpDomain, g.GlbServerCfg.VhostHttpPort))
|
||||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
||||||
})
|
})
|
||||||
@@ -277,7 +287,7 @@ func (pxy *HttpProxy) GetRealConn() (workConn frpNet.Conn, err error) {
|
|||||||
|
|
||||||
var rwc io.ReadWriteCloser = tmpConn
|
var rwc io.ReadWriteCloser = tmpConn
|
||||||
if pxy.cfg.UseEncryption {
|
if pxy.cfg.UseEncryption {
|
||||||
rwc, err = frpIo.WithEncryption(rwc, []byte(config.ServerCommonCfg.PrivilegeToken))
|
rwc, err = frpIo.WithEncryption(rwc, []byte(g.GlbServerCfg.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pxy.Error("create encryption stream error: %v", err)
|
pxy.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
@@ -325,11 +335,11 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
|||||||
l.AddLogPrefix(pxy.name)
|
l.AddLogPrefix(pxy.name)
|
||||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||||
pxy.listeners = append(pxy.listeners, l)
|
pxy.listeners = append(pxy.listeners, l)
|
||||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
|
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, g.GlbServerCfg.VhostHttpsPort))
|
||||||
}
|
}
|
||||||
|
|
||||||
if pxy.cfg.SubDomain != "" {
|
if pxy.cfg.SubDomain != "" {
|
||||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
|
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
|
||||||
l, errRet := pxy.ctl.svr.VhostHttpsMuxer.Listen(routeConfig)
|
l, errRet := pxy.ctl.svr.VhostHttpsMuxer.Listen(routeConfig)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
@@ -338,7 +348,7 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
|||||||
l.AddLogPrefix(pxy.name)
|
l.AddLogPrefix(pxy.name)
|
||||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||||
pxy.listeners = append(pxy.listeners, l)
|
pxy.listeners = append(pxy.listeners, l)
|
||||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
|
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(g.GlbServerCfg.VhostHttpsPort)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pxy.startListenHandler(pxy, HandleUserTcpConnection)
|
pxy.startListenHandler(pxy, HandleUserTcpConnection)
|
||||||
@@ -469,7 +479,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
|
|||||||
|
|
||||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||||
pxy.cfg.RemotePort = pxy.realPort
|
pxy.cfg.RemotePort = pxy.realPort
|
||||||
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.ServerCommonCfg.ProxyBindAddr, pxy.realPort))
|
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", g.GlbServerCfg.ProxyBindAddr, pxy.realPort))
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
@@ -635,7 +645,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn) {
|
|||||||
var local io.ReadWriteCloser = workConn
|
var local io.ReadWriteCloser = workConn
|
||||||
cfg := pxy.GetConf().GetBaseInfo()
|
cfg := pxy.GetConf().GetBaseInfo()
|
||||||
if cfg.UseEncryption {
|
if cfg.UseEncryption {
|
||||||
local, err = frpIo.WithEncryption(local, []byte(config.ServerCommonCfg.PrivilegeToken))
|
local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pxy.Error("create encryption stream error: %v", err)
|
pxy.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/assets"
|
"github.com/fatedier/frp/assets"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
@@ -29,7 +29,7 @@ import (
|
|||||||
"github.com/fatedier/frp/utils/version"
|
"github.com/fatedier/frp/utils/version"
|
||||||
"github.com/fatedier/frp/utils/vhost"
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
|
|
||||||
"github.com/xtaci/smux"
|
fmux "github.com/hashicorp/yamux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -71,13 +71,13 @@ type Service struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewService() (svr *Service, err error) {
|
func NewService() (svr *Service, err error) {
|
||||||
cfg := config.ServerCommonCfg
|
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||||
svr = &Service{
|
svr = &Service{
|
||||||
ctlManager: NewControlManager(),
|
ctlManager: NewControlManager(),
|
||||||
pxyManager: NewProxyManager(),
|
pxyManager: NewProxyManager(),
|
||||||
visitorManager: NewVisitorManager(),
|
visitorManager: NewVisitorManager(),
|
||||||
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||||
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init assets.
|
// Init assets.
|
||||||
@@ -170,7 +170,7 @@ func (svr *Service) Run() {
|
|||||||
if svr.natHoleController != nil {
|
if svr.natHoleController != nil {
|
||||||
go svr.natHoleController.Run()
|
go svr.natHoleController.Run()
|
||||||
}
|
}
|
||||||
if config.ServerCommonCfg.KcpBindPort > 0 {
|
if g.GlbServerCfg.KcpBindPort > 0 {
|
||||||
go svr.HandleListener(svr.kcpListener)
|
go svr.HandleListener(svr.kcpListener)
|
||||||
}
|
}
|
||||||
svr.HandleListener(svr.listener)
|
svr.HandleListener(svr.listener)
|
||||||
@@ -233,8 +233,8 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ServerCommonCfg.TcpMux {
|
if g.GlbServerCfg.TcpMux {
|
||||||
session, err := smux.Server(frpConn, nil)
|
session, err := fmux.Server(frpConn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to create mux connection: %v", err)
|
log.Warn("Failed to create mux connection: %v", err)
|
||||||
frpConn.Close()
|
frpConn.Close()
|
||||||
@@ -270,11 +270,11 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
|
|||||||
|
|
||||||
// Check auth.
|
// Check auth.
|
||||||
nowTime := time.Now().Unix()
|
nowTime := time.Now().Unix()
|
||||||
if config.ServerCommonCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > config.ServerCommonCfg.AuthTimeout {
|
if g.GlbServerCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > g.GlbServerCfg.AuthTimeout {
|
||||||
err = fmt.Errorf("authorization timeout")
|
err = fmt.Errorf("authorization timeout")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if util.GetAuthKey(config.ServerCommonCfg.PrivilegeToken, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
|
if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
|
||||||
err = fmt.Errorf("authorization failed")
|
err = fmt.Errorf("authorization failed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ server_port = 10700
|
|||||||
log_file = ./frpc.log
|
log_file = ./frpc.log
|
||||||
# debug, info, warn, error
|
# debug, info, warn, error
|
||||||
log_level = debug
|
log_level = debug
|
||||||
privilege_token = 123456
|
token = 123456
|
||||||
admin_port = 10600
|
admin_port = 10600
|
||||||
admin_user = abc
|
admin_user = abc
|
||||||
admin_pwd = abc
|
admin_pwd = abc
|
||||||
@@ -161,3 +161,9 @@ remote_port = 0
|
|||||||
type = tcp
|
type = tcp
|
||||||
plugin = http_proxy
|
plugin = http_proxy
|
||||||
remote_port = 0
|
remote_port = 0
|
||||||
|
|
||||||
|
[range:range_tcp]
|
||||||
|
type = tcp
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = 30000-30001,30003
|
||||||
|
remote_port = 30000-30001,30003
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ server_port = 10700
|
|||||||
log_file = ./frpc_visitor.log
|
log_file = ./frpc_visitor.log
|
||||||
# debug, info, warn, error
|
# debug, info, warn, error
|
||||||
log_level = debug
|
log_level = debug
|
||||||
privilege_token = 123456
|
token = 123456
|
||||||
|
|
||||||
[stcp_visitor]
|
[stcp_visitor]
|
||||||
type = stcp
|
type = stcp
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ bind_port = 10700
|
|||||||
vhost_http_port = 10804
|
vhost_http_port = 10804
|
||||||
log_file = ./frps.log
|
log_file = ./frps.log
|
||||||
log_level = debug
|
log_level = debug
|
||||||
privilege_token = 123456
|
token = 123456
|
||||||
privilege_allow_ports = 10000-20000,20002,30000-40000
|
allow_ports = 10000-20000,20002,30000-50000
|
||||||
subdomain_host = sub.com
|
subdomain_host = sub.com
|
||||||
|
|||||||
@@ -53,8 +53,9 @@ var (
|
|||||||
ProxyUdpPortNotAllowed string = "udp_port_not_allowed"
|
ProxyUdpPortNotAllowed string = "udp_port_not_allowed"
|
||||||
ProxyUdpPortNormal string = "udp_port_normal"
|
ProxyUdpPortNormal string = "udp_port_normal"
|
||||||
ProxyUdpRandomPort string = "udp_random_port"
|
ProxyUdpRandomPort string = "udp_random_port"
|
||||||
|
ProxyHttpProxy string = "http_proxy"
|
||||||
|
|
||||||
ProxyHttpProxy string = "http_proxy"
|
ProxyRangeTcpPrefix string = "range_tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -208,7 +209,7 @@ func TestWebSocket(t *testing.T) {
|
|||||||
assert.Equal(TEST_HTTP_NORMAL_STR, string(msg))
|
assert.Equal(TEST_HTTP_NORMAL_STR, string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrivilegeAllowPorts(t *testing.T) {
|
func TestAllowPorts(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
// Port not allowed
|
// Port not allowed
|
||||||
status, err := getProxyStatus(ProxyTcpPortNotAllowed)
|
status, err := getProxyStatus(ProxyTcpPortNotAllowed)
|
||||||
@@ -286,3 +287,15 @@ func TestPluginHttpProxy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRangePortsMapping(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
name := fmt.Sprintf("%s_%d", ProxyRangeTcpPrefix, i)
|
||||||
|
status, err := getProxyStatus(name)
|
||||||
|
if assert.NoError(err) {
|
||||||
|
assert.Equal(client.ProxyStatusRunning, status.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
|
|
||||||
kcp "github.com/xtaci/kcp-go"
|
kcp "github.com/fatedier/kcp-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Conn is the interface of connections used in frp.
|
// Conn is the interface of connections used in frp.
|
||||||
@@ -178,6 +179,7 @@ func (sc *SharedConn) WriteBuff(buffer []byte) (err error) {
|
|||||||
type StatsConn struct {
|
type StatsConn struct {
|
||||||
Conn
|
Conn
|
||||||
|
|
||||||
|
closed int64 // 1 means closed
|
||||||
totalRead int64
|
totalRead int64
|
||||||
totalWrite int64
|
totalWrite int64
|
||||||
statsFunc func(totalRead, totalWrite int64)
|
statsFunc func(totalRead, totalWrite int64)
|
||||||
@@ -203,9 +205,12 @@ func (statsConn *StatsConn) Write(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (statsConn *StatsConn) Close() (err error) {
|
func (statsConn *StatsConn) Close() (err error) {
|
||||||
err = statsConn.Conn.Close()
|
old := atomic.SwapInt64(&statsConn.closed, 1)
|
||||||
if statsConn.statsFunc != nil {
|
if old != 1 {
|
||||||
statsConn.statsFunc(statsConn.totalRead, statsConn.totalWrite)
|
err = statsConn.Conn.Close()
|
||||||
|
if statsConn.statsFunc != nil {
|
||||||
|
statsConn.statsFunc(statsConn.totalRead, statsConn.totalWrite)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandId return a rand string used in frp.
|
// RandId return a rand string used in frp.
|
||||||
@@ -54,3 +56,48 @@ func CanonicalAddr(host string, port int) (addr string) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) {
|
||||||
|
rangeStr = strings.TrimSpace(rangeStr)
|
||||||
|
numbers = make([]int64, 0)
|
||||||
|
// e.g. 1000-2000,2001,2002,3000-4000
|
||||||
|
numRanges := strings.Split(rangeStr, ",")
|
||||||
|
for _, numRangeStr := range numRanges {
|
||||||
|
// 1000-2000 or 2001
|
||||||
|
numArray := strings.Split(numRangeStr, "-")
|
||||||
|
// length: only 1 or 2 is correct
|
||||||
|
rangeType := len(numArray)
|
||||||
|
if rangeType == 1 {
|
||||||
|
// single number
|
||||||
|
singleNum, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
|
||||||
|
if errRet != nil {
|
||||||
|
err = fmt.Errorf("range number is invalid, %v", errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
numbers = append(numbers, singleNum)
|
||||||
|
} else if rangeType == 2 {
|
||||||
|
// range numbers
|
||||||
|
min, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
|
||||||
|
if errRet != nil {
|
||||||
|
err = fmt.Errorf("range number is invalid, %v", errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
max, errRet := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
|
||||||
|
if errRet != nil {
|
||||||
|
err = fmt.Errorf("range number is invalid, %v", errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if max < min {
|
||||||
|
err = fmt.Errorf("range number is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := min; i <= max; i++ {
|
||||||
|
numbers = append(numbers, i)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("range number is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,3 +20,29 @@ func TestGetAuthKey(t *testing.T) {
|
|||||||
t.Log(key)
|
t.Log(key)
|
||||||
assert.Equal("6df41a43725f0c770fd56379e12acf8c", key)
|
assert.Equal("6df41a43725f0c770fd56379e12acf8c", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseRangeNumbers(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
numbers, err := ParseRangeNumbers("2-5")
|
||||||
|
if assert.NoError(err) {
|
||||||
|
assert.Equal([]int64{2, 3, 4, 5}, numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers, err = ParseRangeNumbers("1")
|
||||||
|
if assert.NoError(err) {
|
||||||
|
assert.Equal([]int64{1}, numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers, err = ParseRangeNumbers("3-5,8")
|
||||||
|
if assert.NoError(err) {
|
||||||
|
assert.Equal([]int64{3, 4, 5, 8}, numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers, err = ParseRangeNumbers(" 3-5,8, 10-12 ")
|
||||||
|
if assert.NoError(err) {
|
||||||
|
assert.Equal([]int64{3, 4, 5, 8, 10, 11, 12}, numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseRangeNumbers("3-a")
|
||||||
|
assert.Error(err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.15.1"
|
var version string = "0.18.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func readHandshake(rd io.Reader) (host string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(data) < 2 {
|
if len(data) < 2 {
|
||||||
err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short")
|
err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short", len(data))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
31
vendor/github.com/docopt/docopt-go/.travis.yml
generated
vendored
31
vendor/github.com/docopt/docopt-go/.travis.yml
generated
vendored
@@ -1,31 +0,0 @@
|
|||||||
# Travis CI (http://travis-ci.org/) is a continuous integration
|
|
||||||
# service for open source projects. This file configures it
|
|
||||||
# to run unit tests for docopt-go.
|
|
||||||
|
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- tip
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get golang.org/x/tools/cmd/vet
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
- go get github.com/golang/lint/golint
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -d -v ./... && go build -v ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go vet -x ./...
|
|
||||||
- $HOME/gopath/bin/golint ./...
|
|
||||||
- go test -v ./...
|
|
||||||
- go test -covermode=count -coverprofile=profile.cov .
|
|
||||||
|
|
||||||
after_script:
|
|
||||||
- $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci
|
|
||||||
20
vendor/github.com/docopt/docopt-go/LICENSE
generated
vendored
20
vendor/github.com/docopt/docopt-go/LICENSE
generated
vendored
@@ -1,20 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Keith Batten
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
88
vendor/github.com/docopt/docopt-go/README.md
generated
vendored
88
vendor/github.com/docopt/docopt-go/README.md
generated
vendored
@@ -1,88 +0,0 @@
|
|||||||
docopt-go
|
|
||||||
=========
|
|
||||||
|
|
||||||
[](https://travis-ci.org/docopt/docopt.go)
|
|
||||||
[](https://coveralls.io/r/docopt/docopt.go)
|
|
||||||
[](https://godoc.org/github.com/docopt/docopt.go)
|
|
||||||
|
|
||||||
An implementation of [docopt](http://docopt.org/) in the
|
|
||||||
[Go](http://golang.org/) programming language.
|
|
||||||
|
|
||||||
**docopt** helps you create *beautiful* command-line interfaces easily:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Naval Fate.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
naval_fate ship new <name>...
|
|
||||||
naval_fate ship <name> move <x> <y> [--speed=<kn>]
|
|
||||||
naval_fate ship shoot <x> <y>
|
|
||||||
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
|
|
||||||
naval_fate -h | --help
|
|
||||||
naval_fate --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help Show this screen.
|
|
||||||
--version Show version.
|
|
||||||
--speed=<kn> Speed in knots [default: 10].
|
|
||||||
--moored Moored (anchored) mine.
|
|
||||||
--drifting Drifting mine.`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**docopt** parses command-line arguments based on a help message. Don't
|
|
||||||
write parser code: a good help message already has all the necessary
|
|
||||||
information in it.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
⚠ Use the alias “docopt-go”. To use docopt in your Go code:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/docopt/docopt-go"
|
|
||||||
```
|
|
||||||
|
|
||||||
To install docopt according to your `$GOPATH`:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ go get github.com/docopt/docopt-go
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Parse(doc string, argv []string, help bool, version string,
|
|
||||||
optionsFirst bool, exit ...bool) (map[string]interface{}, error)
|
|
||||||
```
|
|
||||||
Parse `argv` based on the command-line interface described in `doc`.
|
|
||||||
|
|
||||||
Given a conventional command-line help message, docopt creates a parser and
|
|
||||||
processes the arguments. See
|
|
||||||
https://github.com/docopt/docopt#help-message-format for a description of the
|
|
||||||
help message format. If `argv` is `nil`, `os.Args[1:]` is used.
|
|
||||||
|
|
||||||
docopt returns a map of option names to the values parsed from `argv`, and an
|
|
||||||
error or `nil`.
|
|
||||||
|
|
||||||
More documentation for docopt is available at
|
|
||||||
[GoDoc.org](https://godoc.org/github.com/docopt/docopt.go).
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
All tests from the Python version are implemented and passing
|
|
||||||
at [Travis CI](https://travis-ci.org/docopt/docopt.go). New
|
|
||||||
language-agnostic tests have been added
|
|
||||||
to [test_golang.docopt](test_golang.docopt).
|
|
||||||
|
|
||||||
To run tests for docopt-go, use `go test`.
|
|
||||||
1239
vendor/github.com/docopt/docopt-go/docopt.go
generated
vendored
1239
vendor/github.com/docopt/docopt-go/docopt.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1536
vendor/github.com/docopt/docopt-go/docopt_test.go
generated
vendored
1536
vendor/github.com/docopt/docopt-go/docopt_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
37
vendor/github.com/docopt/docopt-go/example_test.go
generated
vendored
37
vendor/github.com/docopt/docopt-go/example_test.go
generated
vendored
@@ -1,37 +0,0 @@
|
|||||||
package docopt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleParse() {
|
|
||||||
usage := `Usage:
|
|
||||||
config_example tcp [<host>] [--force] [--timeout=<seconds>]
|
|
||||||
config_example serial <port> [--baud=<rate>] [--timeout=<seconds>]
|
|
||||||
config_example -h | --help | --version`
|
|
||||||
// parse the command line `comfig_example tcp 127.0.0.1 --force`
|
|
||||||
argv := []string{"tcp", "127.0.0.1", "--force"}
|
|
||||||
arguments, _ := Parse(usage, argv, true, "0.1.1rc", false)
|
|
||||||
// sort the keys of the arguments map
|
|
||||||
var keys []string
|
|
||||||
for k := range arguments {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
// print the argument keys and values
|
|
||||||
for _, k := range keys {
|
|
||||||
fmt.Printf("%9s %v\n", k, arguments[k])
|
|
||||||
}
|
|
||||||
// output:
|
|
||||||
// --baud <nil>
|
|
||||||
// --force true
|
|
||||||
// --help false
|
|
||||||
// --timeout <nil>
|
|
||||||
// --version false
|
|
||||||
// -h false
|
|
||||||
// <host> 127.0.0.1
|
|
||||||
// <port> <nil>
|
|
||||||
// serial false
|
|
||||||
// tcp true
|
|
||||||
}
|
|
||||||
29
vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go
generated
vendored
29
vendor/github.com/docopt/docopt-go/examples/arguments/arguments_example.go
generated
vendored
@@ -1,29 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Usage: arguments_example [-vqrh] [FILE] ...
|
|
||||||
arguments_example (--left | --right) CORRECTION FILE
|
|
||||||
|
|
||||||
Process FILE and optionally apply correction to either left-hand side or
|
|
||||||
right-hand side.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
FILE optional input file
|
|
||||||
CORRECTION correction angle, needs FILE, --left or --right to be present
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help
|
|
||||||
-v verbose mode
|
|
||||||
-q quiet mode
|
|
||||||
-r make report
|
|
||||||
--left use left-hand side
|
|
||||||
--right use right-hand side`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
26
vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go
generated
vendored
26
vendor/github.com/docopt/docopt-go/examples/calculator/calculator_example.go
generated
vendored
@@ -1,26 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Not a serious example.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
calculator_example <value> ( ( + | - | * | / ) <value> )...
|
|
||||||
calculator_example <function> <value> [( , <value> )]...
|
|
||||||
calculator_example (-h | --help)
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
calculator_example 1 + 2 + 3 + 4 + 5
|
|
||||||
calculator_example 1 + 2 '*' 3 / 4 - 5 # note quotes around '*'
|
|
||||||
calculator_example sum 10 , 20 , 30 , 40
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help
|
|
||||||
`
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
76
vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go
generated
vendored
76
vendor/github.com/docopt/docopt-go/examples/config_file/config_file_example.go
generated
vendored
@@ -1,76 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func loadJSONConfig() map[string]interface{} {
|
|
||||||
var result map[string]interface{}
|
|
||||||
jsonData := []byte(`{"--force": true, "--timeout": "10", "--baud": "9600"}`)
|
|
||||||
json.Unmarshal(jsonData, &result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadIniConfig() map[string]interface{} {
|
|
||||||
iniData := `
|
|
||||||
[default-arguments]
|
|
||||||
--force
|
|
||||||
--baud=19200
|
|
||||||
<host>=localhost`
|
|
||||||
// trivial ini parser
|
|
||||||
// default value for an item is bool: true (for --force)
|
|
||||||
// otherwise the value is a string
|
|
||||||
iniParsed := make(map[string]map[string]interface{})
|
|
||||||
var section string
|
|
||||||
for _, line := range strings.Split(iniData, "\n") {
|
|
||||||
if strings.HasPrefix(line, "[") {
|
|
||||||
section = line
|
|
||||||
iniParsed[section] = make(map[string]interface{})
|
|
||||||
} else if section != "" {
|
|
||||||
kv := strings.SplitN(line, "=", 2)
|
|
||||||
if len(kv) == 1 {
|
|
||||||
iniParsed[section][kv[0]] = true
|
|
||||||
} else if len(kv) == 2 {
|
|
||||||
iniParsed[section][kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iniParsed["[default-arguments]"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge combines two maps.
|
|
||||||
// truthiness takes priority over falsiness
|
|
||||||
// mapA takes priority over mapB
|
|
||||||
func merge(mapA, mapB map[string]interface{}) map[string]interface{} {
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
for k, v := range mapA {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range mapB {
|
|
||||||
if _, ok := result[k]; !ok || result[k] == nil || result[k] == false {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Usage:
|
|
||||||
config_file_example tcp [<host>] [--force] [--timeout=<seconds>]
|
|
||||||
config_file_example serial <port> [--baud=<rate>] [--timeout=<seconds>]
|
|
||||||
config_file_example -h | --help | --version`
|
|
||||||
|
|
||||||
jsonConfig := loadJSONConfig()
|
|
||||||
iniConfig := loadIniConfig()
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "0.1.1rc", false)
|
|
||||||
|
|
||||||
// Arguments take priority over INI, INI takes priority over JSON
|
|
||||||
result := merge(arguments, merge(iniConfig, jsonConfig))
|
|
||||||
|
|
||||||
fmt.Println("JSON config: ", jsonConfig)
|
|
||||||
fmt.Println("INI config: ", iniConfig)
|
|
||||||
fmt.Println("Result: ", result)
|
|
||||||
}
|
|
||||||
22
vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go
generated
vendored
22
vendor/github.com/docopt/docopt-go/examples/counted/counted_example.go
generated
vendored
@@ -1,22 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Usage: counted_example --help
|
|
||||||
counted_example -v...
|
|
||||||
counted_example go [go]
|
|
||||||
counted_example (--path=<path>)...
|
|
||||||
counted_example <file> <file>
|
|
||||||
|
|
||||||
Try: counted_example -vvvvvvvvvv
|
|
||||||
counted_example go go
|
|
||||||
counted_example --path ./here --path ./there
|
|
||||||
counted_example this.txt that.txt`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
38
vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go
generated
vendored
38
vendor/github.com/docopt/docopt-go/examples/git/branch/git_branch.go
generated
vendored
@@ -1,38 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git branch [options] [-r | -a] [--merged=<commit> | --no-merged=<commit>]
|
|
||||||
git branch [options] [-l] [-f] <branchname> [<start-point>]
|
|
||||||
git branch [options] [-r] (-d | -D) <branchname>
|
|
||||||
git branch [options] (-m | -M) [<oldbranch>] <newbranch>
|
|
||||||
|
|
||||||
Generic options:
|
|
||||||
-h, --help
|
|
||||||
-v, --verbose show hash and subject, give twice for upstream branch
|
|
||||||
-t, --track set up tracking mode (see git-pull(1))
|
|
||||||
--set-upstream change upstream info
|
|
||||||
--color=<when> use colored output
|
|
||||||
-r act on remote-tracking branches
|
|
||||||
--contains=<commit> print only branches that contain the commit
|
|
||||||
--abbrev=<n> use <n> digits to display SHA-1s
|
|
||||||
|
|
||||||
Specific git-branch actions:
|
|
||||||
-a list both remote-tracking and local branches
|
|
||||||
-d delete fully merged branch
|
|
||||||
-D delete branch (even if not merged)
|
|
||||||
-m move/rename a branch and its reflog
|
|
||||||
-M move/rename a branch, even if target exists
|
|
||||||
-l create the branch's reflog
|
|
||||||
-f, --force force creation (when already exists)
|
|
||||||
--no-merged=<commit> print only not merged branches
|
|
||||||
--merged=<commit> print only merged branches
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
30
vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go
generated
vendored
30
vendor/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go
generated
vendored
@@ -1,30 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git checkout [options] <branch>
|
|
||||||
git checkout [options] <branch> -- <file>...
|
|
||||||
|
|
||||||
options:
|
|
||||||
-q, --quiet suppress progress reporting
|
|
||||||
-b <branch> create and checkout a new branch
|
|
||||||
-B <branch> create/reset and checkout a branch
|
|
||||||
-l create reflog for new branch
|
|
||||||
-t, --track set upstream info for new branch
|
|
||||||
--orphan <new branch>
|
|
||||||
new unparented branch
|
|
||||||
-2, --ours checkout our version for unmerged files
|
|
||||||
-3, --theirs checkout their version for unmerged files
|
|
||||||
-f, --force force checkout (throw away local modifications)
|
|
||||||
-m, --merge perform a 3-way merge with the new branch
|
|
||||||
--conflict <style> conflict style (merge or diff3)
|
|
||||||
-p, --patch select hunks interactively
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
37
vendor/github.com/docopt/docopt-go/examples/git/clone/git_clone.go
generated
vendored
37
vendor/github.com/docopt/docopt-go/examples/git/clone/git_clone.go
generated
vendored
@@ -1,37 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git clone [options] [--] <repo> [<dir>]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-v, --verbose be more verbose
|
|
||||||
-q, --quiet be more quiet
|
|
||||||
--progress force progress reporting
|
|
||||||
-n, --no-checkout don't create a checkout
|
|
||||||
--bare create a bare repository
|
|
||||||
--mirror create a mirror repository (implies bare)
|
|
||||||
-l, --local to clone from a local repository
|
|
||||||
--no-hardlinks don't use local hardlinks, always copy
|
|
||||||
-s, --shared setup as shared repository
|
|
||||||
--recursive initialize submodules in the clone
|
|
||||||
--recurse-submodules initialize submodules in the clone
|
|
||||||
--template <template-directory>
|
|
||||||
directory from which templates will be used
|
|
||||||
--reference <repo> reference repository
|
|
||||||
-o, --origin <branch>
|
|
||||||
use <branch> instead of 'origin' to track upstream
|
|
||||||
-b, --branch <branch>
|
|
||||||
checkout <branch> instead of the remote's HEAD
|
|
||||||
-u, --upload-pack <path>
|
|
||||||
path to git-upload-pack on the remote
|
|
||||||
--depth <depth> create a shallow clone of that depth
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
108
vendor/github.com/docopt/docopt-go/examples/git/git.go
generated
vendored
108
vendor/github.com/docopt/docopt-go/examples/git/git.go
generated
vendored
@@ -1,108 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git [--version] [--exec-path=<path>] [--html-path]
|
|
||||||
[-p|--paginate|--no-pager] [--no-replace-objects]
|
|
||||||
[--bare] [--git-dir=<path>] [--work-tree=<path>]
|
|
||||||
[-c <name>=<value>] [--help]
|
|
||||||
<command> [<args>...]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-c <name=value>
|
|
||||||
-h, --help
|
|
||||||
-p, --paginate
|
|
||||||
|
|
||||||
The most commonly used git commands are:
|
|
||||||
add Add file contents to the index
|
|
||||||
branch List, create, or delete branches
|
|
||||||
checkout Checkout a branch or paths to the working tree
|
|
||||||
clone Clone a repository into a new directory
|
|
||||||
commit Record changes to the repository
|
|
||||||
push Update remote refs along with associated objects
|
|
||||||
remote Manage set of tracked repositories
|
|
||||||
|
|
||||||
See 'git help <command>' for more information on a specific command.
|
|
||||||
`
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "git version 1.7.4.4", true)
|
|
||||||
|
|
||||||
fmt.Println("global arguments:")
|
|
||||||
fmt.Println(args)
|
|
||||||
|
|
||||||
fmt.Println("command arguments:")
|
|
||||||
cmd := args["<command>"].(string)
|
|
||||||
cmdArgs := args["<args>"].([]string)
|
|
||||||
|
|
||||||
err := runCommand(cmd, cmdArgs)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func goRun(scriptName string, args []string) (err error) {
|
|
||||||
cmdArgs := make([]string, 2)
|
|
||||||
cmdArgs[0] = "run"
|
|
||||||
cmdArgs[1] = scriptName
|
|
||||||
cmdArgs = append(cmdArgs, args...)
|
|
||||||
osCmd := exec.Command("go", cmdArgs...)
|
|
||||||
var out []byte
|
|
||||||
out, err = osCmd.Output()
|
|
||||||
fmt.Println(string(out))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCommand(cmd string, args []string) (err error) {
|
|
||||||
argv := make([]string, 1)
|
|
||||||
argv[0] = cmd
|
|
||||||
argv = append(argv, args...)
|
|
||||||
switch cmd {
|
|
||||||
case "add":
|
|
||||||
// subcommand is a function call
|
|
||||||
return cmdAdd(argv)
|
|
||||||
case "branch":
|
|
||||||
// subcommand is a script
|
|
||||||
return goRun("branch/git_branch.go", argv)
|
|
||||||
case "checkout", "clone", "commit", "push", "remote":
|
|
||||||
// subcommand is a script
|
|
||||||
scriptName := fmt.Sprintf("%s/git_%s.go", cmd, cmd)
|
|
||||||
return goRun(scriptName, argv)
|
|
||||||
case "help", "":
|
|
||||||
return goRun("git.go", []string{"git_add.go", "--help"})
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("%s is not a git command. See 'git help'", cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdAdd(argv []string) (err error) {
|
|
||||||
usage := `usage: git add [options] [--] [<filepattern>...]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-h, --help
|
|
||||||
-n, --dry-run dry run
|
|
||||||
-v, --verbose be verbose
|
|
||||||
-i, --interactive interactive picking
|
|
||||||
-p, --patch select hunks interactively
|
|
||||||
-e, --edit edit current diff and apply
|
|
||||||
-f, --force allow adding otherwise ignored files
|
|
||||||
-u, --update update tracked files
|
|
||||||
-N, --intent-to-add record only the fact that the path will be added later
|
|
||||||
-A, --all add all, noticing removal of tracked files
|
|
||||||
--refresh don't add, only refresh the index
|
|
||||||
--ignore-errors just skip files which cannot be added because of errors
|
|
||||||
--ignore-missing check if - even missing - files are ignored in dry run
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
34
vendor/github.com/docopt/docopt-go/examples/git/push/git_push.go
generated
vendored
34
vendor/github.com/docopt/docopt-go/examples/git/push/git_push.go
generated
vendored
@@ -1,34 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git push [options] [<repository> [<refspec>...]]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-h, --help
|
|
||||||
-v, --verbose be more verbose
|
|
||||||
-q, --quiet be more quiet
|
|
||||||
--repo <repository> repository
|
|
||||||
--all push all refs
|
|
||||||
--mirror mirror all refs
|
|
||||||
--delete delete refs
|
|
||||||
--tags push tags (can't be used with --all or --mirror)
|
|
||||||
-n, --dry-run dry run
|
|
||||||
--porcelain machine-readable output
|
|
||||||
-f, --force force updates
|
|
||||||
--thin use thin pack
|
|
||||||
--receive-pack <receive-pack>
|
|
||||||
receive pack program
|
|
||||||
--exec <receive-pack>
|
|
||||||
receive pack program
|
|
||||||
-u, --set-upstream set upstream for git pull/status
|
|
||||||
--progress force progress reporting
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
28
vendor/github.com/docopt/docopt-go/examples/git/remote/git_remote.go
generated
vendored
28
vendor/github.com/docopt/docopt-go/examples/git/remote/git_remote.go
generated
vendored
@@ -1,28 +0,0 @@
|
|||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: git remote [-v | --verbose]
|
|
||||||
git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
|
|
||||||
git remote rename <old> <new>
|
|
||||||
git remote rm <name>
|
|
||||||
git remote set-head <name> (-a | -d | <branch>)
|
|
||||||
git remote [-v | --verbose] show [-n] <name>
|
|
||||||
git remote prune [-n | --dry-run] <name>
|
|
||||||
git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]
|
|
||||||
git remote set-branches <name> [--add] <branch>...
|
|
||||||
git remote set-url <name> <newurl> [<oldurl>]
|
|
||||||
git remote set-url --add <name> <newurl>
|
|
||||||
git remote set-url --delete <name> <url>
|
|
||||||
|
|
||||||
options:
|
|
||||||
-v, --verbose be verbose; must be placed before a subcommand
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
|
||||||
28
vendor/github.com/docopt/docopt-go/examples/naval_fate/naval_fate.go
generated
vendored
28
vendor/github.com/docopt/docopt-go/examples/naval_fate/naval_fate.go
generated
vendored
@@ -1,28 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Naval Fate.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
naval_fate ship new <name>...
|
|
||||||
naval_fate ship <name> move <x> <y> [--speed=<kn>]
|
|
||||||
naval_fate ship shoot <x> <y>
|
|
||||||
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
|
|
||||||
naval_fate -h | --help
|
|
||||||
naval_fate --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help Show this screen.
|
|
||||||
--version Show version.
|
|
||||||
--speed=<kn> Speed in knots [default: 10].
|
|
||||||
--moored Moored (anchored) mine.
|
|
||||||
--drifting Drifting mine.`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
19
vendor/github.com/docopt/docopt-go/examples/odd_even/odd_even_example.go
generated
vendored
19
vendor/github.com/docopt/docopt-go/examples/odd_even/odd_even_example.go
generated
vendored
@@ -1,19 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Usage: odd_even_example [-h | --help] (ODD EVEN)...
|
|
||||||
|
|
||||||
Example, try:
|
|
||||||
odd_even_example 1 2 3 4
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
43
vendor/github.com/docopt/docopt-go/examples/options/options_example.go
generated
vendored
43
vendor/github.com/docopt/docopt-go/examples/options/options_example.go
generated
vendored
@@ -1,43 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Example of program with many options using docopt.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
options_example [-hvqrf NAME] [--exclude=PATTERNS]
|
|
||||||
[--select=ERRORS | --ignore=ERRORS] [--show-source]
|
|
||||||
[--statistics] [--count] [--benchmark] PATH...
|
|
||||||
options_example (--doctest | --testsuite=DIR)
|
|
||||||
options_example --version
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
PATH destination path
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help show this help message and exit
|
|
||||||
--version show version and exit
|
|
||||||
-v --verbose print status messages
|
|
||||||
-q --quiet report only file names
|
|
||||||
-r --repeat show all occurrences of the same error
|
|
||||||
--exclude=PATTERNS exclude files or directories which match these comma
|
|
||||||
separated patterns [default: .svn,CVS,.bzr,.hg,.git]
|
|
||||||
-f NAME --file=NAME when parsing directories, only check filenames matching
|
|
||||||
these comma separated patterns [default: *.go]
|
|
||||||
--select=ERRORS select errors and warnings (e.g. E,W6)
|
|
||||||
--ignore=ERRORS skip errors and warnings (e.g. E4,W)
|
|
||||||
--show-source show source code for each error
|
|
||||||
--statistics count errors and warnings
|
|
||||||
--count print total number of errors and warnings to standard
|
|
||||||
error and set exit code to 1 if total is not null
|
|
||||||
--benchmark measure processing speed
|
|
||||||
--testsuite=DIR run regression tests from dir
|
|
||||||
--doctest run doctest on myself`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "1.0.0rc2", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
24
vendor/github.com/docopt/docopt-go/examples/options_shortcut/options_shortcut_example.go
generated
vendored
24
vendor/github.com/docopt/docopt-go/examples/options_shortcut/options_shortcut_example.go
generated
vendored
@@ -1,24 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Example of program which uses [options] shortcut in pattern.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
options_shortcut_example [options] <port>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help show this help message and exit
|
|
||||||
--version show version and exit
|
|
||||||
-n, --number N use N as a number
|
|
||||||
-t, --timeout TIMEOUT set timeout TIMEOUT seconds
|
|
||||||
--apply apply changes to database
|
|
||||||
-q operate in quiet mode`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "1.0.0rc2", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
16
vendor/github.com/docopt/docopt-go/examples/quick/quick_example.go
generated
vendored
16
vendor/github.com/docopt/docopt-go/examples/quick/quick_example.go
generated
vendored
@@ -1,16 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `Usage:
|
|
||||||
quick_example tcp <host> <port> [--timeout=<seconds>]
|
|
||||||
quick_example serial <port> [--baud=9600] [--timeout=<seconds>]
|
|
||||||
quick_example -h | --help | --version`
|
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "0.1.1rc", false)
|
|
||||||
fmt.Println(arguments)
|
|
||||||
}
|
|
||||||
31
vendor/github.com/docopt/docopt-go/examples/type_assert/type_assert_example.go
generated
vendored
31
vendor/github.com/docopt/docopt-go/examples/type_assert/type_assert_example.go
generated
vendored
@@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
usage := `usage: foo [-x] [-y]`
|
|
||||||
|
|
||||||
arguments, err := docopt.Parse(usage, nil, true, "", false)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(arguments)
|
|
||||||
|
|
||||||
var x = arguments["-x"].(bool) // type assertion required
|
|
||||||
if x == true {
|
|
||||||
fmt.Println("x is true")
|
|
||||||
}
|
|
||||||
|
|
||||||
y := arguments["-y"] // no type assertion needed
|
|
||||||
if y == true {
|
|
||||||
fmt.Println("y is true")
|
|
||||||
}
|
|
||||||
y2 := arguments["-y"]
|
|
||||||
if y2 == 10 { // this will never be true, a type assertion would have produced a build error
|
|
||||||
fmt.Println("y is 10")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
vendor/github.com/docopt/docopt-go/test_golang.docopt
generated
vendored
9
vendor/github.com/docopt/docopt-go/test_golang.docopt
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
r"""usage: prog [NAME_-2]..."""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME_-2": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME_-2": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME_-2": []}
|
|
||||||
957
vendor/github.com/docopt/docopt-go/testcases.docopt
generated
vendored
957
vendor/github.com/docopt/docopt-go/testcases.docopt
generated
vendored
@@ -1,957 +0,0 @@
|
|||||||
r"""Usage: prog
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{}
|
|
||||||
|
|
||||||
$ prog --xxx
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -a All.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"-a": false}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true}
|
|
||||||
|
|
||||||
$ prog -x
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: --all All.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--all": false}
|
|
||||||
|
|
||||||
$ prog --all
|
|
||||||
{"--all": true}
|
|
||||||
|
|
||||||
$ prog --xxx
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -v, --verbose Verbose.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --verbose
|
|
||||||
{"--verbose": true}
|
|
||||||
|
|
||||||
$ prog --ver
|
|
||||||
{"--verbose": true}
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"--verbose": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -p PATH
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -p home/
|
|
||||||
{"-p": "home/"}
|
|
||||||
|
|
||||||
$ prog -phome/
|
|
||||||
{"-p": "home/"}
|
|
||||||
|
|
||||||
$ prog -p
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: --path <path>
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --path home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --path=home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --pa home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --pa=home/
|
|
||||||
{"--path": "home/"}
|
|
||||||
|
|
||||||
$ prog --path
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -p PATH, --path=<path> Path to files.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -proot
|
|
||||||
{"--path": "root"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: -p --path PATH Path to files.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -p root
|
|
||||||
{"--path": "root"}
|
|
||||||
|
|
||||||
$ prog --path root
|
|
||||||
{"--path": "root"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-p PATH Path to files [default: ./]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"-p": "./"}
|
|
||||||
|
|
||||||
$ prog -phome
|
|
||||||
{"-p": "home"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""UsAgE: prog [options]
|
|
||||||
|
|
||||||
OpTiOnS: --path=<files> Path to files
|
|
||||||
[dEfAuLt: /root]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--path": "/root"}
|
|
||||||
|
|
||||||
$ prog --path=home
|
|
||||||
{"--path": "home"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [options]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-a Add
|
|
||||||
-r Remote
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -r -m Hello
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "Hello"}
|
|
||||||
|
|
||||||
$ prog -armyourass
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "yourass"}
|
|
||||||
|
|
||||||
$ prog -a -r
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": null}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
Options: --version
|
|
||||||
--verbose
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --version
|
|
||||||
{"--version": true,
|
|
||||||
"--verbose": false}
|
|
||||||
|
|
||||||
$ prog --verbose
|
|
||||||
{"--version": false,
|
|
||||||
"--verbose": true}
|
|
||||||
|
|
||||||
$ prog --ver
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog --verb
|
|
||||||
{"--version": false,
|
|
||||||
"--verbose": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-a -r -m <msg>]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-a Add
|
|
||||||
-r Remote
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -armyourass
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "yourass"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-armmsg]
|
|
||||||
|
|
||||||
options: -a Add
|
|
||||||
-r Remote
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -r -m Hello
|
|
||||||
{"-a": true,
|
|
||||||
"-r": true,
|
|
||||||
"-m": "Hello"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog -a -b
|
|
||||||
|
|
||||||
options:
|
|
||||||
-a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (-a -b)
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-a] -b
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
{"-a": false, "-b": true}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [(-a -b)]
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -b -a
|
|
||||||
{"-a": true, "-b": true}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-a": false, "-b": false}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (-a|-b)
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true, "-b": false}
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
{"-a": false, "-b": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [ -a | -b ]
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a -b
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-a": false, "-b": false}
|
|
||||||
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true, "-b": false}
|
|
||||||
|
|
||||||
$ prog -b
|
|
||||||
{"-a": false, "-b": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog <arg>"""
|
|
||||||
$ prog 10
|
|
||||||
{"<arg>": "10"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [<arg>]"""
|
|
||||||
$ prog 10
|
|
||||||
{"<arg>": "10"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<arg>": null}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog <kind> <name> <type>"""
|
|
||||||
$ prog 10 20 40
|
|
||||||
{"<kind>": "10", "<name>": "20", "<type>": "40"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog <kind> [<name> <type>]"""
|
|
||||||
$ prog 10 20 40
|
|
||||||
{"<kind>": "10", "<name>": "20", "<type>": "40"}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
{"<kind>": "10", "<name>": "20", "<type>": null}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [<kind> | <name> <type>]"""
|
|
||||||
$ prog 10 20 40
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog 20 40
|
|
||||||
{"<kind>": null, "<name>": "20", "<type>": "40"}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<kind>": null, "<name>": null, "<type>": null}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (<kind> --all | <name>)
|
|
||||||
|
|
||||||
options:
|
|
||||||
--all
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog 10 --all
|
|
||||||
{"<kind>": "10", "--all": true, "<name>": null}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"<kind>": null, "--all": false, "<name>": "10"}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [<name> <name>]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"<name>": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"<name>": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<name>": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [(<name> <name>)]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"<name>": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<name>": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog NAME..."""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [NAME]..."""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [NAME...]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [NAME [NAME ...]]"""
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"]}
|
|
||||||
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (NAME | --foo NAME)
|
|
||||||
|
|
||||||
options: --foo
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": "10", "--foo": false}
|
|
||||||
|
|
||||||
$ prog --foo 10
|
|
||||||
{"NAME": "10", "--foo": true}
|
|
||||||
|
|
||||||
$ prog --foo=10
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog (NAME | --foo) [--bar | NAME]
|
|
||||||
|
|
||||||
options: --foo
|
|
||||||
options: --bar
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog 10
|
|
||||||
{"NAME": ["10"], "--foo": false, "--bar": false}
|
|
||||||
|
|
||||||
$ prog 10 20
|
|
||||||
{"NAME": ["10", "20"], "--foo": false, "--bar": false}
|
|
||||||
|
|
||||||
$ prog --foo --bar
|
|
||||||
{"NAME": [], "--foo": true, "--bar": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Naval Fate.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
prog ship new <name>...
|
|
||||||
prog ship [<name>] move <x> <y> [--speed=<kn>]
|
|
||||||
prog ship shoot <x> <y>
|
|
||||||
prog mine (set|remove) <x> <y> [--moored|--drifting]
|
|
||||||
prog -h | --help
|
|
||||||
prog --version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h --help Show this screen.
|
|
||||||
--version Show version.
|
|
||||||
--speed=<kn> Speed in knots [default: 10].
|
|
||||||
--moored Mored (anchored) mine.
|
|
||||||
--drifting Drifting mine.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog ship Guardian move 150 300 --speed=20
|
|
||||||
{"--drifting": false,
|
|
||||||
"--help": false,
|
|
||||||
"--moored": false,
|
|
||||||
"--speed": "20",
|
|
||||||
"--version": false,
|
|
||||||
"<name>": ["Guardian"],
|
|
||||||
"<x>": "150",
|
|
||||||
"<y>": "300",
|
|
||||||
"mine": false,
|
|
||||||
"move": true,
|
|
||||||
"new": false,
|
|
||||||
"remove": false,
|
|
||||||
"set": false,
|
|
||||||
"ship": true,
|
|
||||||
"shoot": false}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog --hello"""
|
|
||||||
$ prog --hello
|
|
||||||
{"--hello": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [--hello=<world>]"""
|
|
||||||
$ prog
|
|
||||||
{"--hello": null}
|
|
||||||
|
|
||||||
$ prog --hello wrld
|
|
||||||
{"--hello": "wrld"}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-o]"""
|
|
||||||
$ prog
|
|
||||||
{"-o": false}
|
|
||||||
|
|
||||||
$ prog -o
|
|
||||||
{"-o": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [-opr]"""
|
|
||||||
$ prog -op
|
|
||||||
{"-o": true, "-p": true, "-r": false}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog --aabb | --aa"""
|
|
||||||
$ prog --aa
|
|
||||||
{"--aabb": false, "--aa": true}
|
|
||||||
|
|
||||||
$ prog --a
|
|
||||||
"user-error" # not a unique prefix
|
|
||||||
|
|
||||||
#
|
|
||||||
# Counting number of flags
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: prog -v"""
|
|
||||||
$ prog -v
|
|
||||||
{"-v": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [-v -v]"""
|
|
||||||
$ prog
|
|
||||||
{"-v": 0}
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"-v": 1}
|
|
||||||
|
|
||||||
$ prog -vv
|
|
||||||
{"-v": 2}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog -v ..."""
|
|
||||||
$ prog
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"-v": 1}
|
|
||||||
|
|
||||||
$ prog -vv
|
|
||||||
{"-v": 2}
|
|
||||||
|
|
||||||
$ prog -vvvvvv
|
|
||||||
{"-v": 6}
|
|
||||||
|
|
||||||
|
|
||||||
r"""Usage: prog [-v | -vv | -vvv]
|
|
||||||
|
|
||||||
This one is probably most readable user-friednly variant.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"-v": 0}
|
|
||||||
|
|
||||||
$ prog -v
|
|
||||||
{"-v": 1}
|
|
||||||
|
|
||||||
$ prog -vv
|
|
||||||
{"-v": 2}
|
|
||||||
|
|
||||||
$ prog -vvvv
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [--ver --ver]"""
|
|
||||||
$ prog --ver --ver
|
|
||||||
{"--ver": 2}
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Counting commands
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [go]"""
|
|
||||||
$ prog go
|
|
||||||
{"go": true}
|
|
||||||
|
|
||||||
|
|
||||||
r"""usage: prog [go go]"""
|
|
||||||
$ prog
|
|
||||||
{"go": 0}
|
|
||||||
|
|
||||||
$ prog go
|
|
||||||
{"go": 1}
|
|
||||||
|
|
||||||
$ prog go go
|
|
||||||
{"go": 2}
|
|
||||||
|
|
||||||
$ prog go go go
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
r"""usage: prog go..."""
|
|
||||||
$ prog go go go go go
|
|
||||||
{"go": 5}
|
|
||||||
|
|
||||||
#
|
|
||||||
# [options] does not include options from usage-pattern
|
|
||||||
#
|
|
||||||
r"""usage: prog [options] [-a]
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
-b
|
|
||||||
"""
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true, "-b": false}
|
|
||||||
|
|
||||||
$ prog -aa
|
|
||||||
"user-error"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test [options] shourtcut
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: prog [options] A
|
|
||||||
Options:
|
|
||||||
-q Be quiet
|
|
||||||
-v Be verbose.
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog arg
|
|
||||||
{"A": "arg", "-v": false, "-q": false}
|
|
||||||
|
|
||||||
$ prog -v arg
|
|
||||||
{"A": "arg", "-v": true, "-q": false}
|
|
||||||
|
|
||||||
$ prog -q arg
|
|
||||||
{"A": "arg", "-v": false, "-q": true}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test single dash
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [-]"""
|
|
||||||
|
|
||||||
$ prog -
|
|
||||||
{"-": true}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# If argument is repeated, its value should always be a list
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [NAME [NAME ...]]"""
|
|
||||||
|
|
||||||
$ prog a b
|
|
||||||
{"NAME": ["a", "b"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"NAME": []}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Option's argument defaults to null/None
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [options]
|
|
||||||
options:
|
|
||||||
-a Add
|
|
||||||
-m <msg> Message
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a
|
|
||||||
{"-m": null, "-a": true}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test options without description
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog --hello"""
|
|
||||||
$ prog --hello
|
|
||||||
{"--hello": true}
|
|
||||||
|
|
||||||
r"""usage: prog [--hello=<world>]"""
|
|
||||||
$ prog
|
|
||||||
{"--hello": null}
|
|
||||||
|
|
||||||
$ prog --hello wrld
|
|
||||||
{"--hello": "wrld"}
|
|
||||||
|
|
||||||
r"""usage: prog [-o]"""
|
|
||||||
$ prog
|
|
||||||
{"-o": false}
|
|
||||||
|
|
||||||
$ prog -o
|
|
||||||
{"-o": true}
|
|
||||||
|
|
||||||
r"""usage: prog [-opr]"""
|
|
||||||
$ prog -op
|
|
||||||
{"-o": true, "-p": true, "-r": false}
|
|
||||||
|
|
||||||
r"""usage: git [-v | --verbose]"""
|
|
||||||
$ prog -v
|
|
||||||
{"-v": true, "--verbose": false}
|
|
||||||
|
|
||||||
r"""usage: git remote [-v | --verbose]"""
|
|
||||||
$ prog remote -v
|
|
||||||
{"remote": true, "-v": true, "--verbose": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test empty usage pattern
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog"""
|
|
||||||
$ prog
|
|
||||||
{}
|
|
||||||
|
|
||||||
r"""usage: prog
|
|
||||||
prog <a> <b>
|
|
||||||
"""
|
|
||||||
$ prog 1 2
|
|
||||||
{"<a>": "1", "<b>": "2"}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"<a>": null, "<b>": null}
|
|
||||||
|
|
||||||
r"""usage: prog <a> <b>
|
|
||||||
prog
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"<a>": null, "<b>": null}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Option's argument should not capture default value from usage pattern
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [--file=<f>]"""
|
|
||||||
$ prog
|
|
||||||
{"--file": null}
|
|
||||||
|
|
||||||
r"""usage: prog [--file=<f>]
|
|
||||||
|
|
||||||
options: --file <a>
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--file": null}
|
|
||||||
|
|
||||||
r"""Usage: prog [-a <host:port>]
|
|
||||||
|
|
||||||
Options: -a, --address <host:port> TCP address [default: localhost:6283].
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog
|
|
||||||
{"--address": "localhost:6283"}
|
|
||||||
|
|
||||||
#
|
|
||||||
# If option with argument could be repeated,
|
|
||||||
# its arguments should be accumulated into a list
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog --long=<arg> ..."""
|
|
||||||
|
|
||||||
$ prog --long one
|
|
||||||
{"--long": ["one"]}
|
|
||||||
|
|
||||||
$ prog --long one --long two
|
|
||||||
{"--long": ["one", "two"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test multiple elements repeated at once
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog (go <direction> --speed=<km/h>)..."""
|
|
||||||
$ prog go left --speed=5 go right --speed=9
|
|
||||||
{"go": 2, "<direction>": ["left", "right"], "--speed": ["5", "9"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Required options should work with option shortcut
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [options] -a
|
|
||||||
|
|
||||||
options: -a
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -a
|
|
||||||
{"-a": true}
|
|
||||||
|
|
||||||
#
|
|
||||||
# If option could be repeated its defaults should be split into a list
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [-o <o>]...
|
|
||||||
|
|
||||||
options: -o <o> [default: x]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -o this -o that
|
|
||||||
{"-o": ["this", "that"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-o": ["x"]}
|
|
||||||
|
|
||||||
r"""usage: prog [-o <o>]...
|
|
||||||
|
|
||||||
options: -o <o> [default: x y]
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -o this
|
|
||||||
{"-o": ["this"]}
|
|
||||||
|
|
||||||
$ prog
|
|
||||||
{"-o": ["x", "y"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Test stacked option's argument
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog -pPATH
|
|
||||||
|
|
||||||
options: -p PATH
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog -pHOME
|
|
||||||
{"-p": "HOME"}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Issue 56: Repeated mutually exclusive args give nested lists sometimes
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: foo (--xx=x|--yy=y)..."""
|
|
||||||
$ prog --xx=1 --yy=2
|
|
||||||
{"--xx": ["1"], "--yy": ["2"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# POSIXly correct tokenization
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog [<input file>]"""
|
|
||||||
$ prog f.txt
|
|
||||||
{"<input file>": "f.txt"}
|
|
||||||
|
|
||||||
r"""usage: prog [--input=<file name>]..."""
|
|
||||||
$ prog --input a.txt --input=b.txt
|
|
||||||
{"--input": ["a.txt", "b.txt"]}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Issue 85: `[options]` shourtcut with multiple subcommands
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage: prog good [options]
|
|
||||||
prog fail [options]
|
|
||||||
|
|
||||||
options: --loglevel=N
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog fail --loglevel 5
|
|
||||||
{"--loglevel": "5", "fail": true, "good": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Usage-section syntax
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""usage:prog --foo"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true}
|
|
||||||
|
|
||||||
r"""PROGRAM USAGE: prog --foo"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true}
|
|
||||||
|
|
||||||
r"""Usage: prog --foo
|
|
||||||
prog --bar
|
|
||||||
NOT PART OF SECTION"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true, "--bar": false}
|
|
||||||
|
|
||||||
r"""Usage:
|
|
||||||
prog --foo
|
|
||||||
prog --bar
|
|
||||||
|
|
||||||
NOT PART OF SECTION"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true, "--bar": false}
|
|
||||||
|
|
||||||
r"""Usage:
|
|
||||||
prog --foo
|
|
||||||
prog --bar
|
|
||||||
NOT PART OF SECTION"""
|
|
||||||
$ prog --foo
|
|
||||||
{"--foo": true, "--bar": false}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Options-section syntax
|
|
||||||
#
|
|
||||||
|
|
||||||
r"""Usage: prog [options]
|
|
||||||
|
|
||||||
global options: --foo
|
|
||||||
local options: --baz
|
|
||||||
--bar
|
|
||||||
other options:
|
|
||||||
--egg
|
|
||||||
--spam
|
|
||||||
-not-an-option-
|
|
||||||
|
|
||||||
"""
|
|
||||||
$ prog --baz --egg
|
|
||||||
{"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false}
|
|
||||||
1
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
1
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
@@ -8,7 +8,6 @@ matrix:
|
|||||||
- go: 1.6
|
- go: 1.6
|
||||||
- go: 1.7
|
- go: 1.7
|
||||||
- go: 1.8
|
- go: 1.8
|
||||||
- go: 1.9
|
|
||||||
- go: tip
|
- go: tip
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- go: tip
|
||||||
|
|||||||
140
vendor/github.com/gorilla/websocket/client.go
generated
vendored
140
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@@ -5,8 +5,10 @@
|
|||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -86,6 +88,50 @@ type Dialer struct {
|
|||||||
|
|
||||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||||
|
|
||||||
|
// parseURL parses the URL.
|
||||||
|
//
|
||||||
|
// This function is a replacement for the standard library url.Parse function.
|
||||||
|
// In Go 1.4 and earlier, url.Parse loses information from the path.
|
||||||
|
func parseURL(s string) (*url.URL, error) {
|
||||||
|
// From the RFC:
|
||||||
|
//
|
||||||
|
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
|
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
|
var u url.URL
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(s, "ws://"):
|
||||||
|
u.Scheme = "ws"
|
||||||
|
s = s[len("ws://"):]
|
||||||
|
case strings.HasPrefix(s, "wss://"):
|
||||||
|
u.Scheme = "wss"
|
||||||
|
s = s[len("wss://"):]
|
||||||
|
default:
|
||||||
|
return nil, errMalformedURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := strings.Index(s, "?"); i >= 0 {
|
||||||
|
u.RawQuery = s[i+1:]
|
||||||
|
s = s[:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := strings.Index(s, "/"); i >= 0 {
|
||||||
|
u.Opaque = s[i:]
|
||||||
|
s = s[:i]
|
||||||
|
} else {
|
||||||
|
u.Opaque = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
u.Host = s
|
||||||
|
|
||||||
|
if strings.Contains(u.Host, "@") {
|
||||||
|
// Don't bother parsing user information because user information is
|
||||||
|
// not allowed in websocket URIs.
|
||||||
|
return nil, errMalformedURL
|
||||||
|
}
|
||||||
|
|
||||||
|
return &u, nil
|
||||||
|
}
|
||||||
|
|
||||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
||||||
hostPort = u.Host
|
hostPort = u.Host
|
||||||
hostNoPort = u.Host
|
hostNoPort = u.Host
|
||||||
@@ -104,7 +150,7 @@ func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
|||||||
return hostPort, hostNoPort
|
return hostPort, hostNoPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultDialer is a dialer with all fields set to the default values.
|
// DefaultDialer is a dialer with all fields set to the default zero values.
|
||||||
var DefaultDialer = &Dialer{
|
var DefaultDialer = &Dialer{
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
}
|
}
|
||||||
@@ -131,7 +177,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.Parse(urlStr)
|
u, err := parseURL(urlStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -200,52 +246,36 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostPort, hostNoPort := hostPortNoPort(u)
|
||||||
|
|
||||||
|
var proxyURL *url.URL
|
||||||
|
// Check wether the proxy method has been configured
|
||||||
|
if d.Proxy != nil {
|
||||||
|
proxyURL, err = d.Proxy(req)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetHostPort string
|
||||||
|
if proxyURL != nil {
|
||||||
|
targetHostPort, _ = hostPortNoPort(proxyURL)
|
||||||
|
} else {
|
||||||
|
targetHostPort = hostPort
|
||||||
|
}
|
||||||
|
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if d.HandshakeTimeout != 0 {
|
if d.HandshakeTimeout != 0 {
|
||||||
deadline = time.Now().Add(d.HandshakeTimeout)
|
deadline = time.Now().Add(d.HandshakeTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get network dial function.
|
|
||||||
netDial := d.NetDial
|
netDial := d.NetDial
|
||||||
if netDial == nil {
|
if netDial == nil {
|
||||||
netDialer := &net.Dialer{Deadline: deadline}
|
netDialer := &net.Dialer{Deadline: deadline}
|
||||||
netDial = netDialer.Dial
|
netDial = netDialer.Dial
|
||||||
}
|
}
|
||||||
|
|
||||||
// If needed, wrap the dial function to set the connection deadline.
|
netConn, err := netDial("tcp", targetHostPort)
|
||||||
if !deadline.Equal(time.Time{}) {
|
|
||||||
forwardDial := netDial
|
|
||||||
netDial = func(network, addr string) (net.Conn, error) {
|
|
||||||
c, err := forwardDial(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = c.SetDeadline(deadline)
|
|
||||||
if err != nil {
|
|
||||||
c.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If needed, wrap the dial function to connect through a proxy.
|
|
||||||
if d.Proxy != nil {
|
|
||||||
proxyURL, err := d.Proxy(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if proxyURL != nil {
|
|
||||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
netDial = dialer.Dial
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hostPort, hostNoPort := hostPortNoPort(u)
|
|
||||||
netConn, err := netDial("tcp", hostPort)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -256,6 +286,42 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err := netConn.SetDeadline(deadline); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxyURL != nil {
|
||||||
|
connectHeader := make(http.Header)
|
||||||
|
if user := proxyURL.User; user != nil {
|
||||||
|
proxyUser := user.Username()
|
||||||
|
if proxyPassword, passwordSet := user.Password(); passwordSet {
|
||||||
|
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
|
||||||
|
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectReq := &http.Request{
|
||||||
|
Method: "CONNECT",
|
||||||
|
URL: &url.URL{Opaque: hostPort},
|
||||||
|
Host: hostPort,
|
||||||
|
Header: connectHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
connectReq.Write(netConn)
|
||||||
|
|
||||||
|
// Read response.
|
||||||
|
// Okay to use and discard buffered reader here, because
|
||||||
|
// TLS server will not speak until spoken to.
|
||||||
|
br := bufio.NewReader(netConn)
|
||||||
|
resp, err := http.ReadResponse(br, connectReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
f := strings.SplitN(resp.Status, " ", 2)
|
||||||
|
return nil, nil, errors.New(f[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if u.Scheme == "https" {
|
if u.Scheme == "https" {
|
||||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||||
if cfg.ServerName == "" {
|
if cfg.ServerName == "" {
|
||||||
|
|||||||
124
vendor/github.com/gorilla/websocket/client_server_test.go
generated
vendored
124
vendor/github.com/gorilla/websocket/client_server_test.go
generated
vendored
@@ -5,14 +5,11 @@
|
|||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -34,10 +31,9 @@ var cstUpgrader = Upgrader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cstDialer = Dialer{
|
var cstDialer = Dialer{
|
||||||
Subprotocols: []string{"p1", "p2"},
|
Subprotocols: []string{"p1", "p2"},
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
HandshakeTimeout: 30 * time.Second,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cstHandler struct{ *testing.T }
|
type cstHandler struct{ *testing.T }
|
||||||
@@ -147,9 +143,8 @@ func TestProxyDial(t *testing.T) {
|
|||||||
s := newServer(t)
|
s := newServer(t)
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
surl, _ := url.Parse(s.Server.URL)
|
surl, _ := url.Parse(s.URL)
|
||||||
|
|
||||||
cstDialer := cstDialer // make local copy for modification on next line.
|
|
||||||
cstDialer.Proxy = http.ProxyURL(surl)
|
cstDialer.Proxy = http.ProxyURL(surl)
|
||||||
|
|
||||||
connect := false
|
connect := false
|
||||||
@@ -165,8 +160,8 @@ func TestProxyDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !connect {
|
if !connect {
|
||||||
t.Log("connect not received")
|
t.Log("connect not recieved")
|
||||||
http.Error(w, "connect not received", 405)
|
http.Error(w, "connect not recieved", 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
origHandler.ServeHTTP(w, r)
|
origHandler.ServeHTTP(w, r)
|
||||||
@@ -178,16 +173,16 @@ func TestProxyDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer ws.Close()
|
defer ws.Close()
|
||||||
sendRecv(t, ws)
|
sendRecv(t, ws)
|
||||||
|
|
||||||
|
cstDialer.Proxy = http.ProxyFromEnvironment
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyAuthorizationDial(t *testing.T) {
|
func TestProxyAuthorizationDial(t *testing.T) {
|
||||||
s := newServer(t)
|
s := newServer(t)
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
surl, _ := url.Parse(s.Server.URL)
|
surl, _ := url.Parse(s.URL)
|
||||||
surl.User = url.UserPassword("username", "password")
|
surl.User = url.UserPassword("username", "password")
|
||||||
|
|
||||||
cstDialer := cstDialer // make local copy for modification on next line.
|
|
||||||
cstDialer.Proxy = http.ProxyURL(surl)
|
cstDialer.Proxy = http.ProxyURL(surl)
|
||||||
|
|
||||||
connect := false
|
connect := false
|
||||||
@@ -205,8 +200,8 @@ func TestProxyAuthorizationDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !connect {
|
if !connect {
|
||||||
t.Log("connect with proxy authorization not received")
|
t.Log("connect with proxy authorization not recieved")
|
||||||
http.Error(w, "connect with proxy authorization not received", 405)
|
http.Error(w, "connect with proxy authorization not recieved", 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
origHandler.ServeHTTP(w, r)
|
origHandler.ServeHTTP(w, r)
|
||||||
@@ -218,6 +213,8 @@ func TestProxyAuthorizationDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer ws.Close()
|
defer ws.Close()
|
||||||
sendRecv(t, ws)
|
sendRecv(t, ws)
|
||||||
|
|
||||||
|
cstDialer.Proxy = http.ProxyFromEnvironment
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDial(t *testing.T) {
|
func TestDial(t *testing.T) {
|
||||||
@@ -240,7 +237,7 @@ func TestDialCookieJar(t *testing.T) {
|
|||||||
d := cstDialer
|
d := cstDialer
|
||||||
d.Jar = jar
|
d.Jar = jar
|
||||||
|
|
||||||
u, _ := url.Parse(s.URL)
|
u, _ := parseURL(s.URL)
|
||||||
|
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "ws":
|
case "ws":
|
||||||
@@ -249,7 +246,7 @@ func TestDialCookieJar(t *testing.T) {
|
|||||||
u.Scheme = "https"
|
u.Scheme = "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
cookies := []*http.Cookie{{Name: "gorilla", Value: "ws", Path: "/"}}
|
cookies := []*http.Cookie{&http.Cookie{Name: "gorilla", Value: "ws", Path: "/"}}
|
||||||
d.Jar.SetCookies(u, cookies)
|
d.Jar.SetCookies(u, cookies)
|
||||||
|
|
||||||
ws, _, err := d.Dial(s.URL, nil)
|
ws, _, err := d.Dial(s.URL, nil)
|
||||||
@@ -401,17 +398,9 @@ func TestBadMethod(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", s.URL, strings.NewReader(""))
|
resp, err := http.PostForm(s.URL, url.Values{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewRequest returned error %v", err)
|
t.Fatalf("PostForm returned error %v", err)
|
||||||
}
|
|
||||||
req.Header.Set("Connection", "upgrade")
|
|
||||||
req.Header.Set("Upgrade", "websocket")
|
|
||||||
req.Header.Set("Sec-Websocket-Version", "13")
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Do returned error %v", err)
|
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusMethodNotAllowed {
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
@@ -521,82 +510,3 @@ func TestDialCompression(t *testing.T) {
|
|||||||
defer ws.Close()
|
defer ws.Close()
|
||||||
sendRecv(t, ws)
|
sendRecv(t, ws)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSocksProxyDial(t *testing.T) {
|
|
||||||
s := newServer(t)
|
|
||||||
defer s.Close()
|
|
||||||
|
|
||||||
proxyListener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("listen failed: %v", err)
|
|
||||||
}
|
|
||||||
defer proxyListener.Close()
|
|
||||||
go func() {
|
|
||||||
c1, err := proxyListener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("proxy accept failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c1.Close()
|
|
||||||
|
|
||||||
c1.SetDeadline(time.Now().Add(30 * time.Second))
|
|
||||||
|
|
||||||
buf := make([]byte, 32)
|
|
||||||
if _, err := io.ReadFull(c1, buf[:3]); err != nil {
|
|
||||||
t.Errorf("read failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if want := []byte{5, 1, 0}; !bytes.Equal(want, buf[:len(want)]) {
|
|
||||||
t.Errorf("read %x, want %x", buf[:len(want)], want)
|
|
||||||
}
|
|
||||||
if _, err := c1.Write([]byte{5, 0}); err != nil {
|
|
||||||
t.Errorf("write failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(c1, buf[:10]); err != nil {
|
|
||||||
t.Errorf("read failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if want := []byte{5, 1, 0, 1}; !bytes.Equal(want, buf[:len(want)]) {
|
|
||||||
t.Errorf("read %x, want %x", buf[:len(want)], want)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf[1] = 0
|
|
||||||
if _, err := c1.Write(buf[:10]); err != nil {
|
|
||||||
t.Errorf("write failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := net.IP(buf[4:8])
|
|
||||||
port := binary.BigEndian.Uint16(buf[8:10])
|
|
||||||
|
|
||||||
c2, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: int(port)})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("dial failed; %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c2.Close()
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
io.Copy(c1, c2)
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
io.Copy(c2, c1)
|
|
||||||
<-done
|
|
||||||
}()
|
|
||||||
|
|
||||||
purl, err := url.Parse("socks5://" + proxyListener.Addr().String())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("parse failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cstDialer := cstDialer // make local copy for modification on next line.
|
|
||||||
cstDialer.Proxy = http.ProxyURL(purl)
|
|
||||||
|
|
||||||
ws, _, err := cstDialer.Dial(s.URL, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Dial: %v", err)
|
|
||||||
}
|
|
||||||
defer ws.Close()
|
|
||||||
sendRecv(t, ws)
|
|
||||||
}
|
|
||||||
|
|||||||
40
vendor/github.com/gorilla/websocket/client_test.go
generated
vendored
40
vendor/github.com/gorilla/websocket/client_test.go
generated
vendored
@@ -6,9 +6,49 @@ package websocket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var parseURLTests = []struct {
|
||||||
|
s string
|
||||||
|
u *url.URL
|
||||||
|
rui string
|
||||||
|
}{
|
||||||
|
{"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"},
|
||||||
|
{"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"},
|
||||||
|
{"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}, "/"},
|
||||||
|
{"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}, "/"},
|
||||||
|
{"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}, "/a/b"},
|
||||||
|
{"ss://example.com/a/b", nil, ""},
|
||||||
|
{"ws://webmaster@example.com/", nil, ""},
|
||||||
|
{"wss://example.com/a/b?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b", RawQuery: "x=y"}, "/a/b?x=y"},
|
||||||
|
{"wss://example.com?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/", RawQuery: "x=y"}, "/?x=y"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseURL(t *testing.T) {
|
||||||
|
for _, tt := range parseURLTests {
|
||||||
|
u, err := parseURL(tt.s)
|
||||||
|
if tt.u != nil && err != nil {
|
||||||
|
t.Errorf("parseURL(%q) returned error %v", tt.s, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tt.u == nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("parseURL(%q) did not return error", tt.s)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(u, tt.u) {
|
||||||
|
t.Errorf("parseURL(%q) = %v, want %v", tt.s, u, tt.u)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if u.RequestURI() != tt.rui {
|
||||||
|
t.Errorf("parseURL(%q).RequestURI() = %v, want %v", tt.s, u.RequestURI(), tt.rui)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var hostPortNoPortTests = []struct {
|
var hostPortNoPortTests = []struct {
|
||||||
u *url.URL
|
u *url.URL
|
||||||
hostPort, hostNoPort string
|
hostPort, hostNoPort string
|
||||||
|
|||||||
44
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
44
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@@ -76,7 +76,7 @@ const (
|
|||||||
// is UTF-8 encoded text.
|
// is UTF-8 encoded text.
|
||||||
PingMessage = 9
|
PingMessage = 9
|
||||||
|
|
||||||
// PongMessage denotes a pong control message. The optional message payload
|
// PongMessage denotes a ping control message. The optional message payload
|
||||||
// is UTF-8 encoded text.
|
// is UTF-8 encoded text.
|
||||||
PongMessage = 10
|
PongMessage = 10
|
||||||
)
|
)
|
||||||
@@ -100,8 +100,9 @@ func (e *netError) Error() string { return e.msg }
|
|||||||
func (e *netError) Temporary() bool { return e.temporary }
|
func (e *netError) Temporary() bool { return e.temporary }
|
||||||
func (e *netError) Timeout() bool { return e.timeout }
|
func (e *netError) Timeout() bool { return e.timeout }
|
||||||
|
|
||||||
// CloseError represents a close message.
|
// CloseError represents close frame.
|
||||||
type CloseError struct {
|
type CloseError struct {
|
||||||
|
|
||||||
// Code is defined in RFC 6455, section 11.7.
|
// Code is defined in RFC 6455, section 11.7.
|
||||||
Code int
|
Code int
|
||||||
|
|
||||||
@@ -342,8 +343,7 @@ func (c *Conn) Subprotocol() string {
|
|||||||
return c.subprotocol
|
return c.subprotocol
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the underlying network connection without sending or waiting
|
// Close closes the underlying network connection without sending or waiting for a close frame.
|
||||||
// for a close message.
|
|
||||||
func (c *Conn) Close() error {
|
func (c *Conn) Close() error {
|
||||||
return c.conn.Close()
|
return c.conn.Close()
|
||||||
}
|
}
|
||||||
@@ -484,9 +484,6 @@ func (c *Conn) prepWrite(messageType int) error {
|
|||||||
//
|
//
|
||||||
// There can be at most one open writer on a connection. NextWriter closes the
|
// There can be at most one open writer on a connection. NextWriter closes the
|
||||||
// previous writer if the application has not already done so.
|
// previous writer if the application has not already done so.
|
||||||
//
|
|
||||||
// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and
|
|
||||||
// PongMessage) are supported.
|
|
||||||
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
||||||
if err := c.prepWrite(messageType); err != nil {
|
if err := c.prepWrite(messageType); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -767,6 +764,7 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
|
|||||||
// Read methods
|
// Read methods
|
||||||
|
|
||||||
func (c *Conn) advanceFrame() (int, error) {
|
func (c *Conn) advanceFrame() (int, error) {
|
||||||
|
|
||||||
// 1. Skip remainder of previous frame.
|
// 1. Skip remainder of previous frame.
|
||||||
|
|
||||||
if c.readRemaining > 0 {
|
if c.readRemaining > 0 {
|
||||||
@@ -1035,7 +1033,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetReadLimit sets the maximum size for a message read from the peer. If a
|
// SetReadLimit sets the maximum size for a message read from the peer. If a
|
||||||
// message exceeds the limit, the connection sends a close message to the peer
|
// message exceeds the limit, the connection sends a close frame to the peer
|
||||||
// and returns ErrReadLimit to the application.
|
// and returns ErrReadLimit to the application.
|
||||||
func (c *Conn) SetReadLimit(limit int64) {
|
func (c *Conn) SetReadLimit(limit int64) {
|
||||||
c.readLimit = limit
|
c.readLimit = limit
|
||||||
@@ -1048,21 +1046,24 @@ func (c *Conn) CloseHandler() func(code int, text string) error {
|
|||||||
|
|
||||||
// SetCloseHandler sets the handler for close messages received from the peer.
|
// SetCloseHandler sets the handler for close messages received from the peer.
|
||||||
// The code argument to h is the received close code or CloseNoStatusReceived
|
// The code argument to h is the received close code or CloseNoStatusReceived
|
||||||
// if the close message is empty. The default close handler sends a close
|
// if the close message is empty. The default close handler sends a close frame
|
||||||
// message back to the peer.
|
// back to the peer.
|
||||||
//
|
//
|
||||||
// The application must read the connection to process close messages as
|
// The application must read the connection to process close messages as
|
||||||
// described in the section on Control Messages above.
|
// described in the section on Control Frames above.
|
||||||
//
|
//
|
||||||
// The connection read methods return a CloseError when a close message is
|
// The connection read methods return a CloseError when a close frame is
|
||||||
// received. Most applications should handle close messages as part of their
|
// received. Most applications should handle close messages as part of their
|
||||||
// normal error handling. Applications should only set a close handler when the
|
// normal error handling. Applications should only set a close handler when the
|
||||||
// application must perform some action before sending a close message back to
|
// application must perform some action before sending a close frame back to
|
||||||
// the peer.
|
// the peer.
|
||||||
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(code int, text string) error {
|
h = func(code int, text string) error {
|
||||||
message := FormatCloseMessage(code, "")
|
message := []byte{}
|
||||||
|
if code != CloseNoStatusReceived {
|
||||||
|
message = FormatCloseMessage(code, "")
|
||||||
|
}
|
||||||
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1076,11 +1077,11 @@ func (c *Conn) PingHandler() func(appData string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetPingHandler sets the handler for ping messages received from the peer.
|
// SetPingHandler sets the handler for ping messages received from the peer.
|
||||||
// The appData argument to h is the PING message application data. The default
|
// The appData argument to h is the PING frame application data. The default
|
||||||
// ping handler sends a pong to the peer.
|
// ping handler sends a pong to the peer.
|
||||||
//
|
//
|
||||||
// The application must read the connection to process ping messages as
|
// The application must read the connection to process ping messages as
|
||||||
// described in the section on Control Messages above.
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(message string) error {
|
h = func(message string) error {
|
||||||
@@ -1102,11 +1103,11 @@ func (c *Conn) PongHandler() func(appData string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetPongHandler sets the handler for pong messages received from the peer.
|
// SetPongHandler sets the handler for pong messages received from the peer.
|
||||||
// The appData argument to h is the PONG message application data. The default
|
// The appData argument to h is the PONG frame application data. The default
|
||||||
// pong handler does nothing.
|
// pong handler does nothing.
|
||||||
//
|
//
|
||||||
// The application must read the connection to process ping messages as
|
// The application must read the connection to process ping messages as
|
||||||
// described in the section on Control Messages above.
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(string) error { return nil }
|
h = func(string) error { return nil }
|
||||||
@@ -1140,14 +1141,7 @@ func (c *Conn) SetCompressionLevel(level int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
||||||
// An empty message is returned for code CloseNoStatusReceived.
|
|
||||||
func FormatCloseMessage(closeCode int, text string) []byte {
|
func FormatCloseMessage(closeCode int, text string) []byte {
|
||||||
if closeCode == CloseNoStatusReceived {
|
|
||||||
// Return empty message because it's illegal to send
|
|
||||||
// CloseNoStatusReceived. Return non-nil value in case application
|
|
||||||
// checks for nil.
|
|
||||||
return []byte{}
|
|
||||||
}
|
|
||||||
buf := make([]byte, 2+len(text))
|
buf := make([]byte, 2+len(text))
|
||||||
binary.BigEndian.PutUint16(buf, uint16(closeCode))
|
binary.BigEndian.PutUint16(buf, uint16(closeCode))
|
||||||
copy(buf[2:], text)
|
copy(buf[2:], text)
|
||||||
|
|||||||
3
vendor/github.com/gorilla/websocket/conn_test.go
generated
vendored
3
vendor/github.com/gorilla/websocket/conn_test.go
generated
vendored
@@ -341,6 +341,7 @@ func TestUnderlyingConn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBufioReadBytes(t *testing.T) {
|
func TestBufioReadBytes(t *testing.T) {
|
||||||
|
|
||||||
// Test calling bufio.ReadBytes for value longer than read buffer size.
|
// Test calling bufio.ReadBytes for value longer than read buffer size.
|
||||||
|
|
||||||
m := make([]byte, 512)
|
m := make([]byte, 512)
|
||||||
@@ -365,7 +366,7 @@ func TestBufioReadBytes(t *testing.T) {
|
|||||||
t.Fatalf("ReadBytes() returned %v", err)
|
t.Fatalf("ReadBytes() returned %v", err)
|
||||||
}
|
}
|
||||||
if len(p) != len(m) {
|
if len(p) != len(m) {
|
||||||
t.Fatalf("read returned %d bytes, want %d bytes", len(p), len(m))
|
t.Fatalf("read returnd %d bytes, want %d bytes", len(p), len(m))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
45
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
45
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
@@ -6,8 +6,9 @@
|
|||||||
//
|
//
|
||||||
// Overview
|
// Overview
|
||||||
//
|
//
|
||||||
// The Conn type represents a WebSocket connection. A server application calls
|
// The Conn type represents a WebSocket connection. A server application uses
|
||||||
// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:
|
// the Upgrade function from an Upgrader object with a HTTP request handler
|
||||||
|
// to get a pointer to a Conn:
|
||||||
//
|
//
|
||||||
// var upgrader = websocket.Upgrader{
|
// var upgrader = websocket.Upgrader{
|
||||||
// ReadBufferSize: 1024,
|
// ReadBufferSize: 1024,
|
||||||
@@ -30,12 +31,10 @@
|
|||||||
// for {
|
// for {
|
||||||
// messageType, p, err := conn.ReadMessage()
|
// messageType, p, err := conn.ReadMessage()
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// log.Println(err)
|
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
// if err := conn.WriteMessage(messageType, p); err != nil {
|
// if err = conn.WriteMessage(messageType, p); err != nil {
|
||||||
// log.Println(err)
|
// return err
|
||||||
// return
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -86,26 +85,20 @@
|
|||||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
||||||
// methods to send a control message to the peer.
|
// methods to send a control message to the peer.
|
||||||
//
|
//
|
||||||
// Connections handle received close messages by calling the handler function
|
// Connections handle received close messages by sending a close message to the
|
||||||
// set with the SetCloseHandler method and by returning a *CloseError from the
|
// peer and returning a *CloseError from the the NextReader, ReadMessage or the
|
||||||
// NextReader, ReadMessage or the message Read method. The default close
|
// message Read method.
|
||||||
// handler sends a close message to the peer.
|
|
||||||
//
|
//
|
||||||
// Connections handle received ping messages by calling the handler function
|
// Connections handle received ping and pong messages by invoking callback
|
||||||
// set with the SetPingHandler method. The default ping handler sends a pong
|
// functions set with SetPingHandler and SetPongHandler methods. The callback
|
||||||
// message to the peer.
|
// functions are called from the NextReader, ReadMessage and the message Read
|
||||||
|
// methods.
|
||||||
//
|
//
|
||||||
// Connections handle received pong messages by calling the handler function
|
// The default ping handler sends a pong to the peer. The application's reading
|
||||||
// set with the SetPongHandler method. The default pong handler does nothing.
|
// goroutine can block for a short time while the handler writes the pong data
|
||||||
// If an application sends ping messages, then the application should set a
|
// to the connection.
|
||||||
// pong handler to receive the corresponding pong.
|
|
||||||
//
|
//
|
||||||
// The control message handler functions are called from the NextReader,
|
// The application must read the connection to process ping, pong and close
|
||||||
// ReadMessage and message reader Read methods. The default close and ping
|
|
||||||
// handlers can block these methods for a short time when the handler writes to
|
|
||||||
// the connection.
|
|
||||||
//
|
|
||||||
// The application must read the connection to process close, ping and pong
|
|
||||||
// messages sent from the peer. If the application is not otherwise interested
|
// messages sent from the peer. If the application is not otherwise interested
|
||||||
// in messages from the peer, then the application should start a goroutine to
|
// in messages from the peer, then the application should start a goroutine to
|
||||||
// read and discard messages from the peer. A simple example is:
|
// read and discard messages from the peer. A simple example is:
|
||||||
@@ -154,9 +147,9 @@
|
|||||||
// CheckOrigin: func(r *http.Request) bool { return true },
|
// CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The deprecated package-level Upgrade function does not perform origin
|
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||||
// checking. The application is responsible for checking the Origin header
|
// application's responsibility to check the Origin header before calling
|
||||||
// before calling the Upgrade function.
|
// Upgrade.
|
||||||
//
|
//
|
||||||
// Compression EXPERIMENTAL
|
// Compression EXPERIMENTAL
|
||||||
//
|
//
|
||||||
|
|||||||
2
vendor/github.com/gorilla/websocket/examples/chat/README.md
generated
vendored
2
vendor/github.com/gorilla/websocket/examples/chat/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
|||||||
# Chat Example
|
# Chat Example
|
||||||
|
|
||||||
This application shows how to use the
|
This application shows how to use use the
|
||||||
[websocket](https://github.com/gorilla/websocket) package to implement a simple
|
[websocket](https://github.com/gorilla/websocket) package to implement a simple
|
||||||
web chat application.
|
web chat application.
|
||||||
|
|
||||||
|
|||||||
4
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
4
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
@@ -64,7 +64,7 @@ func (c *Client) readPump() {
|
|||||||
for {
|
for {
|
||||||
_, message, err := c.conn.ReadMessage()
|
_, message, err := c.conn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||||
log.Printf("error: %v", err)
|
log.Printf("error: %v", err)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -113,7 +113,7 @@ func (c *Client) writePump() {
|
|||||||
}
|
}
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
|
if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
vendor/github.com/gorilla/websocket/examples/echo/server.go
generated
vendored
1
vendor/github.com/gorilla/websocket/examples/echo/server.go
generated
vendored
@@ -55,7 +55,6 @@ func main() {
|
|||||||
|
|
||||||
var homeTemplate = template.Must(template.New("").Parse(`
|
var homeTemplate = template.Must(template.New("").Parse(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
11
vendor/github.com/gorilla/websocket/json.go
generated
vendored
11
vendor/github.com/gorilla/websocket/json.go
generated
vendored
@@ -9,14 +9,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteJSON writes the JSON encoding of v as a message.
|
// WriteJSON is deprecated, use c.WriteJSON instead.
|
||||||
//
|
|
||||||
// Deprecated: Use c.WriteJSON instead.
|
|
||||||
func WriteJSON(c *Conn, v interface{}) error {
|
func WriteJSON(c *Conn, v interface{}) error {
|
||||||
return c.WriteJSON(v)
|
return c.WriteJSON(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteJSON writes the JSON encoding of v as a message.
|
// WriteJSON writes the JSON encoding of v to the connection.
|
||||||
//
|
//
|
||||||
// See the documentation for encoding/json Marshal for details about the
|
// See the documentation for encoding/json Marshal for details about the
|
||||||
// conversion of Go values to JSON.
|
// conversion of Go values to JSON.
|
||||||
@@ -33,10 +31,7 @@ func (c *Conn) WriteJSON(v interface{}) error {
|
|||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
|
// ReadJSON is deprecated, use c.ReadJSON instead.
|
||||||
// it in the value pointed to by v.
|
|
||||||
//
|
|
||||||
// Deprecated: Use c.ReadJSON instead.
|
|
||||||
func ReadJSON(c *Conn, v interface{}) error {
|
func ReadJSON(c *Conn, v interface{}) error {
|
||||||
return c.ReadJSON(v)
|
return c.ReadJSON(v)
|
||||||
}
|
}
|
||||||
|
|||||||
1
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
1
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
@@ -11,6 +11,7 @@ import "unsafe"
|
|||||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
|
|
||||||
// Mask one byte at a time for small buffers.
|
// Mask one byte at a time for small buffers.
|
||||||
if len(b) < 2*wordSize {
|
if len(b) < 2*wordSize {
|
||||||
for i := range b {
|
for i := range b {
|
||||||
|
|||||||
77
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
77
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
@@ -1,77 +0,0 @@
|
|||||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type netDialerFunc func(netowrk, addr string) (net.Conn, error)
|
|
||||||
|
|
||||||
func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
return fn(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
return &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpProxyDialer struct {
|
|
||||||
proxyURL *url.URL
|
|
||||||
fowardDial func(network, addr string) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
|
|
||||||
hostPort, _ := hostPortNoPort(hpd.proxyURL)
|
|
||||||
conn, err := hpd.fowardDial(network, hostPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
connectHeader := make(http.Header)
|
|
||||||
if user := hpd.proxyURL.User; user != nil {
|
|
||||||
proxyUser := user.Username()
|
|
||||||
if proxyPassword, passwordSet := user.Password(); passwordSet {
|
|
||||||
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
|
|
||||||
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectReq := &http.Request{
|
|
||||||
Method: "CONNECT",
|
|
||||||
URL: &url.URL{Opaque: addr},
|
|
||||||
Host: addr,
|
|
||||||
Header: connectHeader,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := connectReq.Write(conn); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read response. It's OK to use and discard buffered reader here becaue
|
|
||||||
// the remote server does not speak until spoken to.
|
|
||||||
br := bufio.NewReader(conn)
|
|
||||||
resp, err := http.ReadResponse(br, connectReq)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
conn.Close()
|
|
||||||
f := strings.SplitN(resp.Status, " ", 2)
|
|
||||||
return nil, errors.New(f[1])
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
37
vendor/github.com/gorilla/websocket/server.go
generated
vendored
37
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@@ -76,7 +76,7 @@ func checkSameOrigin(r *http.Request) bool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return equalASCIIFold(u.Host, r.Host)
|
return u.Host == r.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
||||||
@@ -104,28 +104,26 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
|
|||||||
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
||||||
// response.
|
// response.
|
||||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
||||||
const badHandshake = "websocket: the client is not using the websocket protocol: "
|
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Method != "GET" {
|
if r.Method != "GET" {
|
||||||
return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
|
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
|
||||||
}
|
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||||
|
}
|
||||||
|
|
||||||
checkOrigin := u.CheckOrigin
|
checkOrigin := u.CheckOrigin
|
||||||
if checkOrigin == nil {
|
if checkOrigin == nil {
|
||||||
checkOrigin = checkSameOrigin
|
checkOrigin = checkSameOrigin
|
||||||
@@ -232,11 +230,10 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
|
|
||||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||||
//
|
//
|
||||||
// Deprecated: Use websocket.Upgrader instead.
|
// This function is deprecated, use websocket.Upgrader instead.
|
||||||
//
|
//
|
||||||
// Upgrade does not perform origin checking. The application is responsible for
|
// The application is responsible for checking the request origin before
|
||||||
// checking the Origin header before calling Upgrade. An example implementation
|
// calling Upgrade. An example implementation of the same origin policy is:
|
||||||
// of the same origin policy check is:
|
|
||||||
//
|
//
|
||||||
// if req.Header.Get("Origin") != "http://"+req.Host {
|
// if req.Header.Get("Origin") != "http://"+req.Host {
|
||||||
// http.Error(w, "Origin not allowed", 403)
|
// http.Error(w, "Origin not allowed", 403)
|
||||||
|
|||||||
18
vendor/github.com/gorilla/websocket/server_test.go
generated
vendored
18
vendor/github.com/gorilla/websocket/server_test.go
generated
vendored
@@ -49,21 +49,3 @@ func TestIsWebSocketUpgrade(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkSameOriginTests = []struct {
|
|
||||||
ok bool
|
|
||||||
r *http.Request
|
|
||||||
}{
|
|
||||||
{false, &http.Request{Host: "example.org", Header: map[string][]string{"Origin": []string{"https://other.org"}}}},
|
|
||||||
{true, &http.Request{Host: "example.org", Header: map[string][]string{"Origin": []string{"https://example.org"}}}},
|
|
||||||
{true, &http.Request{Host: "Example.org", Header: map[string][]string{"Origin": []string{"https://example.org"}}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckSameOrigin(t *testing.T) {
|
|
||||||
for _, tt := range checkSameOriginTests {
|
|
||||||
ok := checkSameOrigin(tt.r)
|
|
||||||
if tt.ok != ok {
|
|
||||||
t.Errorf("checkSameOrigin(%+v) returned %v, want %v", tt.r, ok, tt.ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
33
vendor/github.com/gorilla/websocket/util.go
generated
vendored
33
vendor/github.com/gorilla/websocket/util.go
generated
vendored
@@ -11,7 +11,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
@@ -112,14 +111,14 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
|
|||||||
case escape:
|
case escape:
|
||||||
escape = false
|
escape = false
|
||||||
p[j] = b
|
p[j] = b
|
||||||
j++
|
j += 1
|
||||||
case b == '\\':
|
case b == '\\':
|
||||||
escape = true
|
escape = true
|
||||||
case b == '"':
|
case b == '"':
|
||||||
return string(p[:j]), s[i+1:]
|
return string(p[:j]), s[i+1:]
|
||||||
default:
|
default:
|
||||||
p[j] = b
|
p[j] = b
|
||||||
j++
|
j += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", ""
|
return "", ""
|
||||||
@@ -128,31 +127,8 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
|
|||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// equalASCIIFold returns true if s is equal to t with ASCII case folding.
|
|
||||||
func equalASCIIFold(s, t string) bool {
|
|
||||||
for s != "" && t != "" {
|
|
||||||
sr, size := utf8.DecodeRuneInString(s)
|
|
||||||
s = s[size:]
|
|
||||||
tr, size := utf8.DecodeRuneInString(t)
|
|
||||||
t = t[size:]
|
|
||||||
if sr == tr {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if 'A' <= sr && sr <= 'Z' {
|
|
||||||
sr = sr + 'a' - 'A'
|
|
||||||
}
|
|
||||||
if 'A' <= tr && tr <= 'Z' {
|
|
||||||
tr = tr + 'a' - 'A'
|
|
||||||
}
|
|
||||||
if sr != tr {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s == t
|
|
||||||
}
|
|
||||||
|
|
||||||
// tokenListContainsValue returns true if the 1#token header with the given
|
// tokenListContainsValue returns true if the 1#token header with the given
|
||||||
// name contains a token equal to value with ASCII case folding.
|
// name contains token.
|
||||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||||
headers:
|
headers:
|
||||||
for _, s := range header[name] {
|
for _, s := range header[name] {
|
||||||
@@ -166,7 +142,7 @@ headers:
|
|||||||
if s != "" && s[0] != ',' {
|
if s != "" && s[0] != ',' {
|
||||||
continue headers
|
continue headers
|
||||||
}
|
}
|
||||||
if equalASCIIFold(t, value) {
|
if strings.EqualFold(t, value) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if s == "" {
|
if s == "" {
|
||||||
@@ -180,6 +156,7 @@ headers:
|
|||||||
|
|
||||||
// parseExtensiosn parses WebSocket extensions from a header.
|
// parseExtensiosn parses WebSocket extensions from a header.
|
||||||
func parseExtensions(header http.Header) []map[string]string {
|
func parseExtensions(header http.Header) []map[string]string {
|
||||||
|
|
||||||
// From RFC 6455:
|
// From RFC 6455:
|
||||||
//
|
//
|
||||||
// Sec-WebSocket-Extensions = extension-list
|
// Sec-WebSocket-Extensions = extension-list
|
||||||
|
|||||||
49
vendor/github.com/gorilla/websocket/util_test.go
generated
vendored
49
vendor/github.com/gorilla/websocket/util_test.go
generated
vendored
@@ -10,24 +10,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var equalASCIIFoldTests = []struct {
|
|
||||||
t, s string
|
|
||||||
eq bool
|
|
||||||
}{
|
|
||||||
{"WebSocket", "websocket", true},
|
|
||||||
{"websocket", "WebSocket", true},
|
|
||||||
{"Öyster", "öyster", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqualASCIIFold(t *testing.T) {
|
|
||||||
for _, tt := range equalASCIIFoldTests {
|
|
||||||
eq := equalASCIIFold(tt.s, tt.t)
|
|
||||||
if eq != tt.eq {
|
|
||||||
t.Errorf("equalASCIIFold(%q, %q) = %v, want %v", tt.s, tt.t, eq, tt.eq)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokenListContainsValueTests = []struct {
|
var tokenListContainsValueTests = []struct {
|
||||||
value string
|
value string
|
||||||
ok bool
|
ok bool
|
||||||
@@ -56,32 +38,29 @@ var parseExtensionTests = []struct {
|
|||||||
value string
|
value string
|
||||||
extensions []map[string]string
|
extensions []map[string]string
|
||||||
}{
|
}{
|
||||||
{`foo`, []map[string]string{{"": "foo"}}},
|
{`foo`, []map[string]string{map[string]string{"": "foo"}}},
|
||||||
{`foo, bar; baz=2`, []map[string]string{
|
{`foo, bar; baz=2`, []map[string]string{
|
||||||
{"": "foo"},
|
map[string]string{"": "foo"},
|
||||||
{"": "bar", "baz": "2"}}},
|
map[string]string{"": "bar", "baz": "2"}}},
|
||||||
{`foo; bar="b,a;z"`, []map[string]string{
|
{`foo; bar="b,a;z"`, []map[string]string{
|
||||||
{"": "foo", "bar": "b,a;z"}}},
|
map[string]string{"": "foo", "bar": "b,a;z"}}},
|
||||||
{`foo , bar; baz = 2`, []map[string]string{
|
{`foo , bar; baz = 2`, []map[string]string{
|
||||||
{"": "foo"},
|
map[string]string{"": "foo"},
|
||||||
{"": "bar", "baz": "2"}}},
|
map[string]string{"": "bar", "baz": "2"}}},
|
||||||
{`foo, bar; baz=2 junk`, []map[string]string{
|
{`foo, bar; baz=2 junk`, []map[string]string{
|
||||||
{"": "foo"}}},
|
map[string]string{"": "foo"}}},
|
||||||
{`foo junk, bar; baz=2 junk`, nil},
|
{`foo junk, bar; baz=2 junk`, nil},
|
||||||
{`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{
|
{`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{
|
||||||
{"": "mux", "max-channels": "4", "flow-control": ""},
|
map[string]string{"": "mux", "max-channels": "4", "flow-control": ""},
|
||||||
{"": "deflate-stream"}}},
|
map[string]string{"": "deflate-stream"}}},
|
||||||
{`permessage-foo; x="10"`, []map[string]string{
|
{`permessage-foo; x="10"`, []map[string]string{
|
||||||
{"": "permessage-foo", "x": "10"}}},
|
map[string]string{"": "permessage-foo", "x": "10"}}},
|
||||||
{`permessage-foo; use_y, permessage-foo`, []map[string]string{
|
{`permessage-foo; use_y, permessage-foo`, []map[string]string{
|
||||||
{"": "permessage-foo", "use_y": ""},
|
map[string]string{"": "permessage-foo", "use_y": ""},
|
||||||
{"": "permessage-foo"}}},
|
map[string]string{"": "permessage-foo"}}},
|
||||||
{`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{
|
{`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{
|
||||||
{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"},
|
map[string]string{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"},
|
||||||
{"": "permessage-deflate", "client_max_window_bits": ""}}},
|
map[string]string{"": "permessage-deflate", "client_max_window_bits": ""}}},
|
||||||
{"permessage-deflate; server_no_context_takeover; client_max_window_bits=15", []map[string]string{
|
|
||||||
{"": "permessage-deflate", "server_no_context_takeover": "", "client_max_window_bits": "15"},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseExtensions(t *testing.T) {
|
func TestParseExtensions(t *testing.T) {
|
||||||
|
|||||||
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
@@ -1,473 +0,0 @@
|
|||||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
|
||||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
|
|
||||||
|
|
||||||
// Package proxy provides support for a variety of protocols to proxy network
|
|
||||||
// data.
|
|
||||||
//
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type proxy_direct struct{}
|
|
||||||
|
|
||||||
// Direct is a direct proxy: one that makes network connections directly.
|
|
||||||
var proxy_Direct = proxy_direct{}
|
|
||||||
|
|
||||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
return net.Dial(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PerHost directs connections to a default Dialer unless the host name
|
|
||||||
// requested matches one of a number of exceptions.
|
|
||||||
type proxy_PerHost struct {
|
|
||||||
def, bypass proxy_Dialer
|
|
||||||
|
|
||||||
bypassNetworks []*net.IPNet
|
|
||||||
bypassIPs []net.IP
|
|
||||||
bypassZones []string
|
|
||||||
bypassHosts []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
|
||||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
|
||||||
// the configured rules.
|
|
||||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
|
|
||||||
return &proxy_PerHost{
|
|
||||||
def: defaultDialer,
|
|
||||||
bypass: bypass,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the address addr on the given network through either
|
|
||||||
// defaultDialer or bypass.
|
|
||||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
|
||||||
host, _, err := net.SplitHostPort(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.dialerForRequest(host).Dial(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
for _, net := range p.bypassNetworks {
|
|
||||||
if net.Contains(ip) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bypassIP := range p.bypassIPs {
|
|
||||||
if bypassIP.Equal(ip) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.def
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, zone := range p.bypassZones {
|
|
||||||
if strings.HasSuffix(host, zone) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
if host == zone[1:] {
|
|
||||||
// For a zone ".example.com", we match "example.com"
|
|
||||||
// too.
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bypassHost := range p.bypassHosts {
|
|
||||||
if bypassHost == host {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.def
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFromString parses a string that contains comma-separated values
|
|
||||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
|
||||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
|
||||||
// (localhost). A best effort is made to parse the string and errors are
|
|
||||||
// ignored.
|
|
||||||
func (p *proxy_PerHost) AddFromString(s string) {
|
|
||||||
hosts := strings.Split(s, ",")
|
|
||||||
for _, host := range hosts {
|
|
||||||
host = strings.TrimSpace(host)
|
|
||||||
if len(host) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.Contains(host, "/") {
|
|
||||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
|
||||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
|
||||||
p.AddNetwork(net)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
p.AddIP(ip)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(host, "*.") {
|
|
||||||
p.AddZone(host[1:])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p.AddHost(host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
|
||||||
// this will only take effect if a literal IP address is dialed. A connection
|
|
||||||
// to a named host will never match an IP.
|
|
||||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
|
|
||||||
p.bypassIPs = append(p.bypassIPs, ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
|
||||||
// this will only take effect if a literal IP address is dialed. A connection
|
|
||||||
// to a named host will never match.
|
|
||||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
|
|
||||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
|
||||||
// "example.com" matches "example.com" and all of its subdomains.
|
|
||||||
func (p *proxy_PerHost) AddZone(zone string) {
|
|
||||||
if strings.HasSuffix(zone, ".") {
|
|
||||||
zone = zone[:len(zone)-1]
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(zone, ".") {
|
|
||||||
zone = "." + zone
|
|
||||||
}
|
|
||||||
p.bypassZones = append(p.bypassZones, zone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHost specifies a host name that will use the bypass proxy.
|
|
||||||
func (p *proxy_PerHost) AddHost(host string) {
|
|
||||||
if strings.HasSuffix(host, ".") {
|
|
||||||
host = host[:len(host)-1]
|
|
||||||
}
|
|
||||||
p.bypassHosts = append(p.bypassHosts, host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Dialer is a means to establish a connection.
|
|
||||||
type proxy_Dialer interface {
|
|
||||||
// Dial connects to the given address via the proxy.
|
|
||||||
Dial(network, addr string) (c net.Conn, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auth contains authentication parameters that specific Dialers may require.
|
|
||||||
type proxy_Auth struct {
|
|
||||||
User, Password string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromEnvironment returns the dialer specified by the proxy related variables in
|
|
||||||
// the environment.
|
|
||||||
func proxy_FromEnvironment() proxy_Dialer {
|
|
||||||
allProxy := proxy_allProxyEnv.Get()
|
|
||||||
if len(allProxy) == 0 {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyURL, err := url.Parse(allProxy)
|
|
||||||
if err != nil {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
|
|
||||||
if err != nil {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
|
|
||||||
noProxy := proxy_noProxyEnv.Get()
|
|
||||||
if len(noProxy) == 0 {
|
|
||||||
return proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
perHost := proxy_NewPerHost(proxy, proxy_Direct)
|
|
||||||
perHost.AddFromString(noProxy)
|
|
||||||
return perHost
|
|
||||||
}
|
|
||||||
|
|
||||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
|
||||||
// from a URL with such a scheme.
|
|
||||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
|
|
||||||
|
|
||||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
|
||||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
|
||||||
// by FromURL.
|
|
||||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
|
|
||||||
if proxy_proxySchemes == nil {
|
|
||||||
proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
|
|
||||||
}
|
|
||||||
proxy_proxySchemes[scheme] = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromURL returns a Dialer given a URL specification and an underlying
|
|
||||||
// Dialer for it to make network requests.
|
|
||||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
var auth *proxy_Auth
|
|
||||||
if u.User != nil {
|
|
||||||
auth = new(proxy_Auth)
|
|
||||||
auth.User = u.User.Username()
|
|
||||||
if p, ok := u.User.Password(); ok {
|
|
||||||
auth.Password = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch u.Scheme {
|
|
||||||
case "socks5":
|
|
||||||
return proxy_SOCKS5("tcp", u.Host, auth, forward)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
|
||||||
// was registered by another package.
|
|
||||||
if proxy_proxySchemes != nil {
|
|
||||||
if f, ok := proxy_proxySchemes[u.Scheme]; ok {
|
|
||||||
return f(u, forward)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
proxy_allProxyEnv = &proxy_envOnce{
|
|
||||||
names: []string{"ALL_PROXY", "all_proxy"},
|
|
||||||
}
|
|
||||||
proxy_noProxyEnv = &proxy_envOnce{
|
|
||||||
names: []string{"NO_PROXY", "no_proxy"},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// envOnce looks up an environment variable (optionally by multiple
|
|
||||||
// names) once. It mitigates expensive lookups on some platforms
|
|
||||||
// (e.g. Windows).
|
|
||||||
// (Borrowed from net/http/transport.go)
|
|
||||||
type proxy_envOnce struct {
|
|
||||||
names []string
|
|
||||||
once sync.Once
|
|
||||||
val string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *proxy_envOnce) Get() string {
|
|
||||||
e.once.Do(e.init)
|
|
||||||
return e.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *proxy_envOnce) init() {
|
|
||||||
for _, n := range e.names {
|
|
||||||
e.val = os.Getenv(n)
|
|
||||||
if e.val != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
|
||||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
|
||||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
s := &proxy_socks5{
|
|
||||||
network: network,
|
|
||||||
addr: addr,
|
|
||||||
forward: forward,
|
|
||||||
}
|
|
||||||
if auth != nil {
|
|
||||||
s.user = auth.User
|
|
||||||
s.password = auth.Password
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type proxy_socks5 struct {
|
|
||||||
user, password string
|
|
||||||
network, addr string
|
|
||||||
forward proxy_Dialer
|
|
||||||
}
|
|
||||||
|
|
||||||
const proxy_socks5Version = 5
|
|
||||||
|
|
||||||
const (
|
|
||||||
proxy_socks5AuthNone = 0
|
|
||||||
proxy_socks5AuthPassword = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const proxy_socks5Connect = 1
|
|
||||||
|
|
||||||
const (
|
|
||||||
proxy_socks5IP4 = 1
|
|
||||||
proxy_socks5Domain = 3
|
|
||||||
proxy_socks5IP6 = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
var proxy_socks5Errors = []string{
|
|
||||||
"",
|
|
||||||
"general failure",
|
|
||||||
"connection forbidden",
|
|
||||||
"network unreachable",
|
|
||||||
"host unreachable",
|
|
||||||
"connection refused",
|
|
||||||
"TTL expired",
|
|
||||||
"command not supported",
|
|
||||||
"address type not supported",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
|
||||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
switch network {
|
|
||||||
case "tcp", "tcp6", "tcp4":
|
|
||||||
default:
|
|
||||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := s.forward.Dial(s.network, s.addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := s.connect(conn, addr); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect takes an existing connection to a socks5 proxy server,
|
|
||||||
// and commands the server to extend that connection to target,
|
|
||||||
// which must be a canonical address with a host and port.
|
|
||||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
|
|
||||||
host, portStr, err := net.SplitHostPort(target)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
port, err := strconv.Atoi(portStr)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
|
||||||
}
|
|
||||||
if port < 1 || port > 0xffff {
|
|
||||||
return errors.New("proxy: port number out of range: " + portStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the size here is just an estimate
|
|
||||||
buf := make([]byte, 0, 6+len(host))
|
|
||||||
|
|
||||||
buf = append(buf, proxy_socks5Version)
|
|
||||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
|
||||||
buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
if buf[0] != 5 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
|
||||||
}
|
|
||||||
if buf[1] == 0xff {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
|
||||||
}
|
|
||||||
|
|
||||||
// See RFC 1929
|
|
||||||
if buf[1] == proxy_socks5AuthPassword {
|
|
||||||
buf = buf[:0]
|
|
||||||
buf = append(buf, 1 /* password protocol version */)
|
|
||||||
buf = append(buf, uint8(len(s.user)))
|
|
||||||
buf = append(buf, s.user...)
|
|
||||||
buf = append(buf, uint8(len(s.password)))
|
|
||||||
buf = append(buf, s.password...)
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf[1] != 0 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf[:0]
|
|
||||||
buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
|
|
||||||
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
|
||||||
buf = append(buf, proxy_socks5IP4)
|
|
||||||
ip = ip4
|
|
||||||
} else {
|
|
||||||
buf = append(buf, proxy_socks5IP6)
|
|
||||||
}
|
|
||||||
buf = append(buf, ip...)
|
|
||||||
} else {
|
|
||||||
if len(host) > 255 {
|
|
||||||
return errors.New("proxy: destination host name too long: " + host)
|
|
||||||
}
|
|
||||||
buf = append(buf, proxy_socks5Domain)
|
|
||||||
buf = append(buf, byte(len(host)))
|
|
||||||
buf = append(buf, host...)
|
|
||||||
}
|
|
||||||
buf = append(buf, byte(port>>8), byte(port))
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
failure := "unknown error"
|
|
||||||
if int(buf[1]) < len(proxy_socks5Errors) {
|
|
||||||
failure = proxy_socks5Errors[buf[1]]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(failure) > 0 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesToDiscard := 0
|
|
||||||
switch buf[3] {
|
|
||||||
case proxy_socks5IP4:
|
|
||||||
bytesToDiscard = net.IPv4len
|
|
||||||
case proxy_socks5IP6:
|
|
||||||
bytesToDiscard = net.IPv6len
|
|
||||||
case proxy_socks5Domain:
|
|
||||||
_, err := io.ReadFull(conn, buf[:1])
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
bytesToDiscard = int(buf[0])
|
|
||||||
default:
|
|
||||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cap(buf) < bytesToDiscard {
|
|
||||||
buf = make([]byte, bytesToDiscard)
|
|
||||||
} else {
|
|
||||||
buf = buf[:bytesToDiscard]
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also need to discard the port number
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -21,4 +21,3 @@ _testmain.go
|
|||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.prof
|
|
||||||
362
vendor/github.com/hashicorp/yamux/LICENSE
generated
vendored
Normal file
362
vendor/github.com/hashicorp/yamux/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
||||||
86
vendor/github.com/hashicorp/yamux/README.md
generated
vendored
Normal file
86
vendor/github.com/hashicorp/yamux/README.md
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Yamux
|
||||||
|
|
||||||
|
Yamux (Yet another Multiplexer) is a multiplexing library for Golang.
|
||||||
|
It relies on an underlying connection to provide reliability
|
||||||
|
and ordering, such as TCP or Unix domain sockets, and provides
|
||||||
|
stream-oriented multiplexing. It is inspired by SPDY but is not
|
||||||
|
interoperable with it.
|
||||||
|
|
||||||
|
Yamux features include:
|
||||||
|
|
||||||
|
* Bi-directional streams
|
||||||
|
* Streams can be opened by either client or server
|
||||||
|
* Useful for NAT traversal
|
||||||
|
* Server-side push support
|
||||||
|
* Flow control
|
||||||
|
* Avoid starvation
|
||||||
|
* Back-pressure to prevent overwhelming a receiver
|
||||||
|
* Keep Alives
|
||||||
|
* Enables persistent connections over a load balancer
|
||||||
|
* Efficient
|
||||||
|
* Enables thousands of logical streams with low overhead
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For complete documentation, see the associated [Godoc](http://godoc.org/github.com/hashicorp/yamux).
|
||||||
|
|
||||||
|
## Specification
|
||||||
|
|
||||||
|
The full specification for Yamux is provided in the `spec.md` file.
|
||||||
|
It can be used as a guide to implementors of interoperable libraries.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Using Yamux is remarkably simple:
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
func client() {
|
||||||
|
// Get a TCP connection
|
||||||
|
conn, err := net.Dial(...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup client side of yamux
|
||||||
|
session, err := yamux.Client(conn, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a new stream
|
||||||
|
stream, err := session.Open()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream implements net.Conn
|
||||||
|
stream.Write([]byte("ping"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func server() {
|
||||||
|
// Accept a TCP connection
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup server side of yamux
|
||||||
|
session, err := yamux.Server(conn, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept a stream
|
||||||
|
stream, err := session.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for a message
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
stream.Read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
60
vendor/github.com/hashicorp/yamux/addr.go
generated
vendored
Normal file
60
vendor/github.com/hashicorp/yamux/addr.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package yamux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hasAddr is used to get the address from the underlying connection
|
||||||
|
type hasAddr interface {
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
RemoteAddr() net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// yamuxAddr is used when we cannot get the underlying address
|
||||||
|
type yamuxAddr struct {
|
||||||
|
Addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*yamuxAddr) Network() string {
|
||||||
|
return "yamux"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (y *yamuxAddr) String() string {
|
||||||
|
return fmt.Sprintf("yamux:%s", y.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr is used to get the address of the listener.
|
||||||
|
func (s *Session) Addr() net.Addr {
|
||||||
|
return s.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr is used to get the local address of the
|
||||||
|
// underlying connection.
|
||||||
|
func (s *Session) LocalAddr() net.Addr {
|
||||||
|
addr, ok := s.conn.(hasAddr)
|
||||||
|
if !ok {
|
||||||
|
return &yamuxAddr{"local"}
|
||||||
|
}
|
||||||
|
return addr.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr is used to get the address of remote end
|
||||||
|
// of the underlying connection
|
||||||
|
func (s *Session) RemoteAddr() net.Addr {
|
||||||
|
addr, ok := s.conn.(hasAddr)
|
||||||
|
if !ok {
|
||||||
|
return &yamuxAddr{"remote"}
|
||||||
|
}
|
||||||
|
return addr.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr returns the local address
|
||||||
|
func (s *Stream) LocalAddr() net.Addr {
|
||||||
|
return s.session.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr returns the remote address
|
||||||
|
func (s *Stream) RemoteAddr() net.Addr {
|
||||||
|
return s.session.RemoteAddr()
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user