mirror of
				https://github.com/fatedier/frp.git
				synced 2025-11-04 00:27:20 +00:00 
			
		
		
		
	Compare commits
	
		
			19 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ea62bc5a34 | ||
| 
						 | 
					23bb76397a | ||
| 
						 | 
					859a330e6c | ||
| 
						 | 
					86ac511763 | ||
| 
						 | 
					f2e98ef8a4 | ||
| 
						 | 
					495d999b6c | ||
| 
						 | 
					6d1af85e80 | ||
| 
						 | 
					1db091b381 | ||
| 
						 | 
					0b9124d4fd | ||
| 
						 | 
					6c6607ae68 | ||
| 
						 | 
					83d80857fd | ||
| 
						 | 
					98fa3855bd | ||
| 
						 | 
					9440bc5d72 | ||
| 
						 | 
					95753ebf1c | ||
| 
						 | 
					f8c6795119 | ||
| 
						 | 
					7033f3e72b | ||
| 
						 | 
					e3101b7aa8 | ||
| 
						 | 
					c747f160aa | ||
| 
						 | 
					c8748a2948 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -27,6 +27,7 @@ _testmain.go
 | 
			
		||||
bin/
 | 
			
		||||
packages/
 | 
			
		||||
test/bin/
 | 
			
		||||
vendor/
 | 
			
		||||
 | 
			
		||||
# Cache
 | 
			
		||||
*.swp
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
    - 1.12.x
 | 
			
		||||
    - 1.13.x
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
    - make
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,5 @@
 | 
			
		||||
export PATH := $(GOPATH)/bin:$(PATH)
 | 
			
		||||
export GO111MODULE=on
 | 
			
		||||
 | 
			
		||||
all: fmt build
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								README.md
									
									
									
									
									
								
							@@ -30,7 +30,11 @@ frp also has a P2P connect mode.
 | 
			
		||||
    * [Using Environment Variables](#using-environment-variables)
 | 
			
		||||
    * [Dashboard](#dashboard)
 | 
			
		||||
    * [Admin UI](#admin-ui)
 | 
			
		||||
    * [Monitor](#monitor)
 | 
			
		||||
        * [Prometheus](#prometheus)
 | 
			
		||||
    * [Authenticating the Client](#authenticating-the-client)
 | 
			
		||||
        * [Token Authentication](#token-authentication)
 | 
			
		||||
        * [OIDC Authentication](#oidc-authentication)
 | 
			
		||||
    * [Encryption and Compression](#encryption-and-compression)
 | 
			
		||||
        * [TLS](#tls)
 | 
			
		||||
    * [Hot-Reloading frpc configuration](#hot-reloading-frpc-configuration)
 | 
			
		||||
@@ -49,9 +53,10 @@ frp also has a P2P connect mode.
 | 
			
		||||
    * [Get Real IP](#get-real-ip)
 | 
			
		||||
        * [HTTP X-Forwarded-For](#http-x-forwarded-for)
 | 
			
		||||
        * [Proxy Protocol](#proxy-protocol)
 | 
			
		||||
    * [Require HTTP Basic auth (password) for web services](#require-http-basic-auth-password-for-web-services)
 | 
			
		||||
    * [Custom subdomain names](#custom-subdomain-names)
 | 
			
		||||
    * [URL routing](#url-routing)
 | 
			
		||||
    * [Require HTTP Basic Auth (Password) for Web Services](#require-http-basic-auth-password-for-web-services)
 | 
			
		||||
    * [Custom Subdomain Names](#custom-subdomain-names)
 | 
			
		||||
    * [URL Routing](#url-routing)
 | 
			
		||||
    * [TCP Port Multiplexing](#tcp-port-multiplexing)
 | 
			
		||||
    * [Connecting to frps via HTTP PROXY](#connecting-to-frps-via-http-proxy)
 | 
			
		||||
    * [Range ports mapping](#range-ports-mapping)
 | 
			
		||||
    * [Client Plugins](#client-plugins)
 | 
			
		||||
@@ -435,9 +440,59 @@ admin_pwd = admin
 | 
			
		||||
 | 
			
		||||
Then visit `http://127.0.0.1:7400` to see admin UI, with username and password both being `admin` by default.
 | 
			
		||||
 | 
			
		||||
### Monitor
 | 
			
		||||
 | 
			
		||||
When dashboard is enabled, frps will save monitor data in cache. It will be cleared after process restart.
 | 
			
		||||
 | 
			
		||||
Prometheus is also supported.
 | 
			
		||||
 | 
			
		||||
#### Prometheus
 | 
			
		||||
 | 
			
		||||
Enable dashboard first, then configure `enable_prometheus = true` in `frps.ini`.
 | 
			
		||||
 | 
			
		||||
`http://{dashboard_addr}/metrics` will provide prometheus monitor data.
 | 
			
		||||
 | 
			
		||||
### Authenticating the Client
 | 
			
		||||
 | 
			
		||||
Always use the same `token` in the `[common]` section in `frps.ini` and `frpc.ini`.
 | 
			
		||||
There are 2 authentication methods to authenticate frpc with frps. 
 | 
			
		||||
 | 
			
		||||
You can decide which one to use by configuring `authentication_method` under `[common]` in `frpc.ini` and `frps.ini`.
 | 
			
		||||
 | 
			
		||||
Configuring `authenticate_heartbeats = true` under `[common]` will use the configured authentication method to add and validate authentication on every heartbeat between frpc and frps.
 | 
			
		||||
 | 
			
		||||
Configuring `authenticate_new_work_conns = true` under `[common]` will do the same for every new work connection between frpc and frps.
 | 
			
		||||
 | 
			
		||||
#### Token Authentication
 | 
			
		||||
 | 
			
		||||
When specifying `authentication_method = token` under `[common]` in `frpc.ini` and `frps.ini` - token based authentication will be used.
 | 
			
		||||
 | 
			
		||||
Make sure to specify the same `token` in the `[common]` section in `frps.ini` and `frpc.ini` for frpc to pass frps validation
 | 
			
		||||
 | 
			
		||||
#### OIDC Authentication
 | 
			
		||||
 | 
			
		||||
When specifying `authentication_method = oidc` under `[common]` in `frpc.ini` and `frps.ini` - OIDC based authentication will be used.
 | 
			
		||||
 | 
			
		||||
OIDC stands for OpenID Connect, and the flow used is called [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4).
 | 
			
		||||
 | 
			
		||||
To use this authentication type - configure `frpc.ini` and `frps.ini` as follows:
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# frps.ini
 | 
			
		||||
[common]
 | 
			
		||||
authentication_method = oidc
 | 
			
		||||
oidc_issuer = https://example-oidc-issuer.com/
 | 
			
		||||
oidc_audience = https://oidc-audience.com/.default
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# frpc.ini
 | 
			
		||||
[common]
 | 
			
		||||
authentication_method = oidc
 | 
			
		||||
oidc_client_id = 98692467-37de-409a-9fac-bb2585826f18 # Replace with OIDC client ID
 | 
			
		||||
oidc_client_secret = oidc_secret
 | 
			
		||||
oidc_audience = https://oidc-audience.com/.default
 | 
			
		||||
oidc_token_endpoint_url = https://example-oidc-endpoint.com/oauth2/v2.0/token
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Encryption and Compression
 | 
			
		||||
 | 
			
		||||
@@ -461,6 +516,8 @@ Config `tls_enable = true` in the `[common]` section to `frpc.ini` to enable thi
 | 
			
		||||
 | 
			
		||||
For port multiplexing, frp sends a first byte `0x17` to dial a TLS connection.
 | 
			
		||||
 | 
			
		||||
To enforce `frps` to only accept TLS connections - configure `tls_only = true` in the `[common]` section in `frps.ini`.
 | 
			
		||||
 | 
			
		||||
### Hot-Reloading frpc configuration
 | 
			
		||||
 | 
			
		||||
The `admin_addr` and `admin_port` fields are required for enabling HTTP API:
 | 
			
		||||
@@ -712,7 +769,7 @@ proxy_protocol_version = v2
 | 
			
		||||
 | 
			
		||||
You can enable Proxy Protocol support in nginx to expose user's real IP in HTTP header `X-Real-IP`, and then read `X-Real-IP` header in your web service for the real IP.
 | 
			
		||||
 | 
			
		||||
### Require HTTP Basic auth (password) for web services
 | 
			
		||||
### Require HTTP Basic Auth (Password) for Web Services
 | 
			
		||||
 | 
			
		||||
Anyone who can guess your tunnel URL can access your local web server unless you protect it with a password.
 | 
			
		||||
 | 
			
		||||
@@ -732,7 +789,7 @@ http_pwd = abc
 | 
			
		||||
 | 
			
		||||
Visit `http://test.example.com` in the browser and now you are prompted to enter the username and password.
 | 
			
		||||
 | 
			
		||||
### Custom subdomain names
 | 
			
		||||
### Custom Subdomain Names
 | 
			
		||||
 | 
			
		||||
It is convenient to use `subdomain` configure for http and https types when many people share one frps server.
 | 
			
		||||
 | 
			
		||||
@@ -755,7 +812,7 @@ Now you can visit your web service on `test.frps.com`.
 | 
			
		||||
 | 
			
		||||
Note that if `subdomain_host` is not empty, `custom_domains` should not be the subdomain of `subdomain_host`.
 | 
			
		||||
 | 
			
		||||
### URL routing
 | 
			
		||||
### URL Routing
 | 
			
		||||
 | 
			
		||||
frp supports forwarding HTTP requests to different backend web services by url routing.
 | 
			
		||||
 | 
			
		||||
@@ -778,6 +835,49 @@ locations = /news,/about
 | 
			
		||||
 | 
			
		||||
HTTP requests with URL prefix `/news` or `/about` will be forwarded to **web02** and other requests to **web01**.
 | 
			
		||||
 | 
			
		||||
### TCP Port Multiplexing
 | 
			
		||||
 | 
			
		||||
frp supports receiving TCP sockets directed to different proxies on a single port on frps, similar to `vhost_http_port` and `vhost_https_port`.
 | 
			
		||||
 | 
			
		||||
The only supported TCP port multiplexing method available at the moment is `httpconnect` - HTTP CONNECT tunnel.
 | 
			
		||||
 | 
			
		||||
When setting `tcpmux_httpconnect_port` to anything other than 0 in frps under `[common]`, frps will listen on this port for HTTP CONNECT requests.
 | 
			
		||||
 | 
			
		||||
The host of the HTTP CONNECT request will be used to match the proxy in frps. Proxy hosts can be configured in frpc by configuring `custom_domain` and / or `subdomain` under `type = tcpmux` proxies, when `multiplexer = httpconnect`.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# frps.ini
 | 
			
		||||
[common]
 | 
			
		||||
bind_port = 7000
 | 
			
		||||
tcpmux_httpconnect_port = 1337
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# frpc.ini
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = x.x.x.x
 | 
			
		||||
server_port = 7000
 | 
			
		||||
 | 
			
		||||
[proxy1]
 | 
			
		||||
type = tcpmux
 | 
			
		||||
multiplexer = httpconnect
 | 
			
		||||
custom_domains = test1
 | 
			
		||||
 | 
			
		||||
[proxy2]
 | 
			
		||||
type = tcpmux
 | 
			
		||||
multiplexer = httpconnect
 | 
			
		||||
custom_domains = test2
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In the above configuration - frps can be contacted on port 1337 with a HTTP CONNECT header such as:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
CONNECT test1 HTTP/1.1\r\n\r\n
 | 
			
		||||
```
 | 
			
		||||
and the connection will be routed to `proxy1`.
 | 
			
		||||
 | 
			
		||||
### Connecting to frps via HTTP PROXY
 | 
			
		||||
 | 
			
		||||
frpc can connect to frps using HTTP proxy if you set OS environment variable `HTTP_PROXY`, or if `http_proxy` is set in frpc.ini file.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										104
									
								
								README_zh.md
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								README_zh.md
									
									
									
									
									
								
							@@ -26,7 +26,11 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
 | 
			
		||||
    * [配置文件模版渲染](#配置文件模版渲染)
 | 
			
		||||
    * [Dashboard](#dashboard)
 | 
			
		||||
    * [Admin UI](#admin-ui)
 | 
			
		||||
    * [身份验证](#身份验证)
 | 
			
		||||
    * [监控](#监控)
 | 
			
		||||
        * [Prometheus](#prometheus)
 | 
			
		||||
    * [客户端身份验证](#客户端身份验证)
 | 
			
		||||
        * [Token](#token)
 | 
			
		||||
        * [OIDC](#oidc)
 | 
			
		||||
    * [加密与压缩](#加密与压缩)
 | 
			
		||||
        * [TLS](#tls)
 | 
			
		||||
    * [客户端热加载配置文件](#客户端热加载配置文件)
 | 
			
		||||
@@ -48,6 +52,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
 | 
			
		||||
    * [通过密码保护你的 web 服务](#通过密码保护你的-web-服务)
 | 
			
		||||
    * [自定义二级域名](#自定义二级域名)
 | 
			
		||||
    * [URL 路由](#url-路由)
 | 
			
		||||
    * [TCP 端口复用类型](#tcp-端口复用类型)
 | 
			
		||||
    * [通过代理连接 frps](#通过代理连接-frps)
 | 
			
		||||
    * [范围端口映射](#范围端口映射)
 | 
			
		||||
    * [客户端插件](#客户端插件)
 | 
			
		||||
@@ -459,9 +464,56 @@ admin_pwd = admin
 | 
			
		||||
 | 
			
		||||
如果想要在外网环境访问 Admin UI,将 7400 端口映射出去即可,但需要重视安全风险。
 | 
			
		||||
 | 
			
		||||
### 身份验证
 | 
			
		||||
### 监控
 | 
			
		||||
 | 
			
		||||
服务端和客户端的 common 配置中的 `token` 参数一致则身份验证通过。
 | 
			
		||||
frps 当启用 Dashboard 后,会默认开启内部的监控,数据存放在内存中,每次重启进程后会清空,监控数据可以通过 dashboard 的地址发送 HTTP 请求获取。
 | 
			
		||||
 | 
			
		||||
目前还支持 Prometheus 作为可选的监控系统。
 | 
			
		||||
 | 
			
		||||
#### Prometheus
 | 
			
		||||
 | 
			
		||||
在 `frps.ini` 中启用 Dashboard,并且设置 `enable_prometheus = true`,则通过 `http://{dashboard_addr}/metrics` 可以获取到 Prometheus 的监控数据。
 | 
			
		||||
 | 
			
		||||
### 客户端身份验证
 | 
			
		||||
 | 
			
		||||
目前 frpc 和 frps 之间支持两种身份验证方式,`token` 和 `oidc`。
 | 
			
		||||
 | 
			
		||||
通过 `frpc.ini` 和 `frps.ini` 中 `[common]` section 的 `authentication_method` 参数配置需要使用的验证方法。
 | 
			
		||||
 | 
			
		||||
`authenticate_heartbeats = true` 将会在每一个心跳包中附加上鉴权信息。
 | 
			
		||||
 | 
			
		||||
`authenticate_new_work_conns = true` 将会在每次建立新的工作连接时附加上鉴权信息。
 | 
			
		||||
 | 
			
		||||
#### Token
 | 
			
		||||
 | 
			
		||||
当 `authentication_method = token`,将会启用基于 token 的验证方式。
 | 
			
		||||
 | 
			
		||||
需要在 `frpc.ini` 和 `frps.ini` 的 `[common]` section 中设置相同的 `token`。
 | 
			
		||||
 | 
			
		||||
#### OIDC
 | 
			
		||||
 | 
			
		||||
当 `authentication_method = oidc`,将会启用基于 OIDC 的身份验证。
 | 
			
		||||
 | 
			
		||||
验证流程参考 [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4)
 | 
			
		||||
 | 
			
		||||
启用这一验证方式,配置 `frpc.ini` 和 `frps.ini` 如下:
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# frps.ini
 | 
			
		||||
[common]
 | 
			
		||||
authentication_method = oidc
 | 
			
		||||
oidc_issuer = https://example-oidc-issuer.com/
 | 
			
		||||
oidc_audience = https://oidc-audience.com/.default
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
[common]
 | 
			
		||||
authentication_method = oidc
 | 
			
		||||
oidc_client_id = 98692467-37de-409a-9fac-bb2585826f18 # Replace with OIDC client ID
 | 
			
		||||
oidc_client_secret = oidc_secret
 | 
			
		||||
oidc_audience = https://oidc-audience.com/.default
 | 
			
		||||
oidc_token_endpoint_url = https://example-oidc-endpoint.com/oauth2/v2.0/token
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 加密与压缩
 | 
			
		||||
 | 
			
		||||
@@ -487,6 +539,8 @@ use_compression = true
 | 
			
		||||
 | 
			
		||||
为了端口复用,frp 建立 TLS 连接的第一个字节为 0x17。
 | 
			
		||||
 | 
			
		||||
通过将 frps.ini 的 `[common]` 中 `tls_only` 设置为 true,可以强制 frps 只接受 TLS 连接。
 | 
			
		||||
 | 
			
		||||
**注意: 启用此功能后除 xtcp 外,不需要再设置 use_encryption。**
 | 
			
		||||
 | 
			
		||||
### 客户端热加载配置文件
 | 
			
		||||
@@ -824,6 +878,50 @@ locations = /news,/about
 | 
			
		||||
 | 
			
		||||
按照上述的示例配置后,`web.yourdomain.com` 这个域名下所有以 `/news` 以及 `/about` 作为前缀的 URL 请求都会被转发到 web02,其余的请求会被转发到 web01。
 | 
			
		||||
 | 
			
		||||
### TCP 端口复用类型
 | 
			
		||||
 | 
			
		||||
frp 支持将单个端口收到的连接路由到不同的代理,类似 `vhost_http_port` 和 `vhost_https_port`。
 | 
			
		||||
 | 
			
		||||
目前支持的复用器只有 `httpconnect`。
 | 
			
		||||
 | 
			
		||||
当在 `frps.ini` 的 `[common]` 中设置 `tcpmux_httpconnect_port`,frps 将会监听在这个端口,接收 HTTP CONNECT 请求。
 | 
			
		||||
 | 
			
		||||
frps 会根据 HTTP CONNECT 请求中的 host 路由到不同的后端代理。
 | 
			
		||||
 | 
			
		||||
示例配置如下:
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# frps.ini
 | 
			
		||||
[common]
 | 
			
		||||
bind_port = 7000
 | 
			
		||||
tcpmux_httpconnect_port = 1337
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# frpc.ini
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = x.x.x.x
 | 
			
		||||
server_port = 7000
 | 
			
		||||
 | 
			
		||||
[proxy1]
 | 
			
		||||
type = tcpmux
 | 
			
		||||
multiplexer = httpconnect
 | 
			
		||||
custom_domains = test1
 | 
			
		||||
 | 
			
		||||
[proxy2]
 | 
			
		||||
type = tcpmux
 | 
			
		||||
multiplexer = httpconnect
 | 
			
		||||
custom_domains = test2
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
通过上面的配置,frps 如果接收到 HTTP CONNECT 请求内容:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
CONNECT test1 HTTP/1.1\r\n\r\n
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
该连接将会被路由到 proxy1 。
 | 
			
		||||
 | 
			
		||||
### 通过代理连接 frps
 | 
			
		||||
 | 
			
		||||
在只能通过代理访问外网的环境内,frpc 支持通过 HTTP PROXY 和 frps 进行通信。
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/client/proxy"
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
	frpNet "github.com/fatedier/frp/utils/net"
 | 
			
		||||
@@ -82,13 +83,17 @@ type Control struct {
 | 
			
		||||
 | 
			
		||||
	// service context
 | 
			
		||||
	ctx context.Context
 | 
			
		||||
 | 
			
		||||
	// sets authentication based on selected method
 | 
			
		||||
	authSetter auth.Setter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewControl(ctx context.Context, runId string, conn net.Conn, session *fmux.Session,
 | 
			
		||||
	clientCfg config.ClientCommonConf,
 | 
			
		||||
	pxyCfgs map[string]config.ProxyConf,
 | 
			
		||||
	visitorCfgs map[string]config.VisitorConf,
 | 
			
		||||
	serverUDPPort int) *Control {
 | 
			
		||||
	serverUDPPort int,
 | 
			
		||||
	authSetter auth.Setter) *Control {
 | 
			
		||||
 | 
			
		||||
	// new xlog instance
 | 
			
		||||
	ctl := &Control{
 | 
			
		||||
@@ -107,6 +112,7 @@ func NewControl(ctx context.Context, runId string, conn net.Conn, session *fmux.
 | 
			
		||||
		serverUDPPort:      serverUDPPort,
 | 
			
		||||
		xl:                 xlog.FromContextSafe(ctx),
 | 
			
		||||
		ctx:                ctx,
 | 
			
		||||
		authSetter:         authSetter,
 | 
			
		||||
	}
 | 
			
		||||
	ctl.pm = proxy.NewProxyManager(ctl.ctx, ctl.sendCh, clientCfg, serverUDPPort)
 | 
			
		||||
 | 
			
		||||
@@ -136,6 +142,10 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
 | 
			
		||||
	m := &msg.NewWorkConn{
 | 
			
		||||
		RunId: ctl.runId,
 | 
			
		||||
	}
 | 
			
		||||
	if err = ctl.authSetter.SetNewWorkConn(m); err != nil {
 | 
			
		||||
		xl.Warn("error during NewWorkConn authentication: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err = msg.WriteMsg(workConn, m); err != nil {
 | 
			
		||||
		xl.Warn("work connection write to server error: %v", err)
 | 
			
		||||
		workConn.Close()
 | 
			
		||||
@@ -148,6 +158,11 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
 | 
			
		||||
		workConn.Close()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if startMsg.Error != "" {
 | 
			
		||||
		xl.Error("StartWorkConn contains error: %s", startMsg.Error)
 | 
			
		||||
		workConn.Close()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// dispatch this work connection to related proxy
 | 
			
		||||
	ctl.pm.HandleWorkConn(startMsg.ProxyName, workConn, &startMsg)
 | 
			
		||||
@@ -282,7 +297,12 @@ func (ctl *Control) msgHandler() {
 | 
			
		||||
		case <-hbSend.C:
 | 
			
		||||
			// send heartbeat to server
 | 
			
		||||
			xl.Debug("send heartbeat to server")
 | 
			
		||||
			ctl.sendCh <- &msg.Ping{}
 | 
			
		||||
			pingMsg := &msg.Ping{}
 | 
			
		||||
			if err := ctl.authSetter.SetPing(pingMsg); err != nil {
 | 
			
		||||
				xl.Warn("error during ping authentication: %v", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctl.sendCh <- pingMsg
 | 
			
		||||
		case <-hbCheck.C:
 | 
			
		||||
			if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartBeatTimeout)*time.Second {
 | 
			
		||||
				xl.Warn("heartbeat timeout")
 | 
			
		||||
@@ -301,6 +321,11 @@ func (ctl *Control) msgHandler() {
 | 
			
		||||
			case *msg.NewProxyResp:
 | 
			
		||||
				ctl.HandleNewProxyResp(m)
 | 
			
		||||
			case *msg.Pong:
 | 
			
		||||
				if m.Error != "" {
 | 
			
		||||
					xl.Error("Pong contains error: %s", m.Error)
 | 
			
		||||
					ctl.conn.Close()
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				ctl.lastPong = time.Now()
 | 
			
		||||
				xl.Debug("receive heartbeat from server")
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,11 @@ func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.Cl
 | 
			
		||||
			BaseProxy: &baseProxy,
 | 
			
		||||
			cfg:       cfg,
 | 
			
		||||
		}
 | 
			
		||||
	case *config.TcpMuxProxyConf:
 | 
			
		||||
		pxy = &TcpMuxProxy{
 | 
			
		||||
			BaseProxy: &baseProxy,
 | 
			
		||||
			cfg:       cfg,
 | 
			
		||||
		}
 | 
			
		||||
	case *config.UdpProxyConf:
 | 
			
		||||
		pxy = &UdpProxy{
 | 
			
		||||
			BaseProxy: &baseProxy,
 | 
			
		||||
@@ -141,6 +146,35 @@ func (pxy *TcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
 | 
			
		||||
		conn, []byte(pxy.clientCfg.Token), m)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TCP Multiplexer
 | 
			
		||||
type TcpMuxProxy struct {
 | 
			
		||||
	*BaseProxy
 | 
			
		||||
 | 
			
		||||
	cfg         *config.TcpMuxProxyConf
 | 
			
		||||
	proxyPlugin plugin.Plugin
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) Run() (err error) {
 | 
			
		||||
	if pxy.cfg.Plugin != "" {
 | 
			
		||||
		pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) Close() {
 | 
			
		||||
	if pxy.proxyPlugin != nil {
 | 
			
		||||
		pxy.proxyPlugin.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
 | 
			
		||||
	HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter,
 | 
			
		||||
		conn, []byte(pxy.clientCfg.Token), m)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTP
 | 
			
		||||
type HttpProxy struct {
 | 
			
		||||
	*BaseProxy
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,11 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/assets"
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
	"github.com/fatedier/frp/utils/log"
 | 
			
		||||
	frpNet "github.com/fatedier/frp/utils/net"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
	"github.com/fatedier/frp/utils/version"
 | 
			
		||||
	"github.com/fatedier/frp/utils/xlog"
 | 
			
		||||
 | 
			
		||||
@@ -46,6 +46,9 @@ type Service struct {
 | 
			
		||||
	ctl   *Control
 | 
			
		||||
	ctlMu sync.RWMutex
 | 
			
		||||
 | 
			
		||||
	// Sets authentication based on selected method
 | 
			
		||||
	authSetter auth.Setter
 | 
			
		||||
 | 
			
		||||
	cfg         config.ClientCommonConf
 | 
			
		||||
	pxyCfgs     map[string]config.ProxyConf
 | 
			
		||||
	visitorCfgs map[string]config.VisitorConf
 | 
			
		||||
@@ -70,6 +73,7 @@ func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
	svr = &Service{
 | 
			
		||||
		authSetter:  auth.NewAuthSetter(cfg.AuthClientConfig),
 | 
			
		||||
		cfg:         cfg,
 | 
			
		||||
		cfgFile:     cfgFile,
 | 
			
		||||
		pxyCfgs:     pxyCfgs,
 | 
			
		||||
@@ -105,7 +109,7 @@ func (svr *Service) Run() error {
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// login success
 | 
			
		||||
			ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
 | 
			
		||||
			ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
 | 
			
		||||
			ctl.Run()
 | 
			
		||||
			svr.ctlMu.Lock()
 | 
			
		||||
			svr.ctl = ctl
 | 
			
		||||
@@ -159,7 +163,7 @@ func (svr *Service) keepControllerWorking() {
 | 
			
		||||
			// reconnect success, init delayTime
 | 
			
		||||
			delayTime = time.Second
 | 
			
		||||
 | 
			
		||||
			ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
 | 
			
		||||
			ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
 | 
			
		||||
			ctl.Run()
 | 
			
		||||
			svr.ctlMu.Lock()
 | 
			
		||||
			svr.ctl = ctl
 | 
			
		||||
@@ -212,17 +216,20 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
 | 
			
		||||
		conn = stream
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	now := time.Now().Unix()
 | 
			
		||||
	loginMsg := &msg.Login{
 | 
			
		||||
		Arch:         runtime.GOARCH,
 | 
			
		||||
		Os:           runtime.GOOS,
 | 
			
		||||
		PoolCount:    svr.cfg.PoolCount,
 | 
			
		||||
		User:         svr.cfg.User,
 | 
			
		||||
		Version:      version.Full(),
 | 
			
		||||
		PrivilegeKey: util.GetAuthKey(svr.cfg.Token, now),
 | 
			
		||||
		Timestamp:    now,
 | 
			
		||||
		RunId:        svr.runId,
 | 
			
		||||
		Metas:        svr.cfg.Metas,
 | 
			
		||||
		Arch:      runtime.GOARCH,
 | 
			
		||||
		Os:        runtime.GOOS,
 | 
			
		||||
		PoolCount: svr.cfg.PoolCount,
 | 
			
		||||
		User:      svr.cfg.User,
 | 
			
		||||
		Version:   version.Full(),
 | 
			
		||||
		Timestamp: time.Now().Unix(),
 | 
			
		||||
		RunId:     svr.runId,
 | 
			
		||||
		Metas:     svr.cfg.Metas,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add auth
 | 
			
		||||
	if err = svr.authSetter.SetLogin(loginMsg); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = msg.WriteMsg(conn, loginMsg); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ import (
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/client"
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/utils/log"
 | 
			
		||||
	"github.com/fatedier/frp/utils/version"
 | 
			
		||||
@@ -65,6 +66,7 @@ var (
 | 
			
		||||
	hostHeaderRewrite string
 | 
			
		||||
	role              string
 | 
			
		||||
	sk                string
 | 
			
		||||
	multiplexer       string
 | 
			
		||||
	serverName        string
 | 
			
		||||
	bindAddr          string
 | 
			
		||||
	bindPort          int
 | 
			
		||||
@@ -157,7 +159,6 @@ func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
 | 
			
		||||
 | 
			
		||||
	cfg.User = user
 | 
			
		||||
	cfg.Protocol = protocol
 | 
			
		||||
	cfg.Token = token
 | 
			
		||||
	cfg.LogLevel = logLevel
 | 
			
		||||
	cfg.LogFile = logFile
 | 
			
		||||
	cfg.LogMaxDays = int64(logMaxDays)
 | 
			
		||||
@@ -168,6 +169,10 @@ func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
 | 
			
		||||
	}
 | 
			
		||||
	cfg.DisableLogColor = disableLogColor
 | 
			
		||||
 | 
			
		||||
	// Only token authentication is supported in cmd mode
 | 
			
		||||
	cfg.AuthClientConfig = auth.GetDefaultAuthClientConf()
 | 
			
		||||
	cfg.Token = token
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								cmd/frpc/sub/tcpmux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								cmd/frpc/sub/tcpmux.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
// Copyright 2020 guylewin, guy@lewin.co.il
 | 
			
		||||
//
 | 
			
		||||
// 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() {
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
 | 
			
		||||
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().StringVarP(&multiplexer, "mux", "", "", "multiplexer")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
 | 
			
		||||
	tcpMuxCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
 | 
			
		||||
 | 
			
		||||
	rootCmd.AddCommand(tcpMuxCmd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var tcpMuxCmd = &cobra.Command{
 | 
			
		||||
	Use:   "tcpmux",
 | 
			
		||||
	Short: "Run frpc with a single tcpmux proxy",
 | 
			
		||||
	RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
		clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println(err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cfg := &config.TcpMuxProxyConf{}
 | 
			
		||||
		var prefix string
 | 
			
		||||
		if user != "" {
 | 
			
		||||
			prefix = user + "."
 | 
			
		||||
		}
 | 
			
		||||
		cfg.ProxyName = prefix + proxyName
 | 
			
		||||
		cfg.ProxyType = consts.TcpMuxProxy
 | 
			
		||||
		cfg.LocalIp = localIp
 | 
			
		||||
		cfg.LocalPort = localPort
 | 
			
		||||
		cfg.CustomDomains = strings.Split(customDomains, ",")
 | 
			
		||||
		cfg.SubDomain = subDomain
 | 
			
		||||
		cfg.Multiplexer = multiplexer
 | 
			
		||||
		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(clientCfg, proxyConfs, nil, "")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println(err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
@@ -21,6 +21,7 @@ import (
 | 
			
		||||
	"github.com/fatedier/golib/crypto"
 | 
			
		||||
 | 
			
		||||
	_ "github.com/fatedier/frp/assets/frps/statik"
 | 
			
		||||
	_ "github.com/fatedier/frp/models/metrics"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/server"
 | 
			
		||||
	"github.com/fatedier/frp/utils/log"
 | 
			
		||||
@@ -171,8 +172,11 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
 | 
			
		||||
	cfg.LogFile = logFile
 | 
			
		||||
	cfg.LogLevel = logLevel
 | 
			
		||||
	cfg.LogMaxDays = logMaxDays
 | 
			
		||||
	cfg.Token = token
 | 
			
		||||
	cfg.SubDomainHost = subDomainHost
 | 
			
		||||
 | 
			
		||||
	// Only token authentication is supported in cmd mode
 | 
			
		||||
	cfg.AuthServerConfig = auth.GetDefaultAuthServerConf()
 | 
			
		||||
	cfg.Token = token
 | 
			
		||||
	if len(allowPorts) > 0 {
 | 
			
		||||
		// e.g. 1000-2000,2001,2002,3000-4000
 | 
			
		||||
		ports, errRet := util.ParseRangeNumbers(allowPorts)
 | 
			
		||||
 
 | 
			
		||||
@@ -264,3 +264,10 @@ bind_addr = 127.0.0.1
 | 
			
		||||
bind_port = 9001
 | 
			
		||||
use_encryption = false
 | 
			
		||||
use_compression = false
 | 
			
		||||
 | 
			
		||||
[tcpmuxhttpconnect]
 | 
			
		||||
type = tcpmux
 | 
			
		||||
multiplexer = httpconnect
 | 
			
		||||
local_ip = 127.0.0.1
 | 
			
		||||
local_port = 10701
 | 
			
		||||
custom_domains = tunnel1
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,12 @@ vhost_https_port = 443
 | 
			
		||||
# response header timeout(seconds) for vhost http server, default is 60s
 | 
			
		||||
# vhost_http_timeout = 60
 | 
			
		||||
 | 
			
		||||
# TcpMuxHttpConnectPort specifies the port that the server listens for TCP
 | 
			
		||||
# HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
 | 
			
		||||
# requests on one single port. If it's not - it will listen on this value for
 | 
			
		||||
# HTTP CONNECT requests. By default, this value is 0.
 | 
			
		||||
# tcpmux_httpconnect_port = 1337
 | 
			
		||||
 | 
			
		||||
# set dashboard_addr and dashboard_port to view dashboard of frps
 | 
			
		||||
# dashboard_addr's default value is same with bind_addr
 | 
			
		||||
# dashboard is available only if dashboard_port is set
 | 
			
		||||
@@ -33,6 +39,9 @@ dashboard_port = 7500
 | 
			
		||||
dashboard_user = admin
 | 
			
		||||
dashboard_pwd = admin
 | 
			
		||||
 | 
			
		||||
# enable_prometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} in /metrics api.
 | 
			
		||||
enable_prometheus = true
 | 
			
		||||
 | 
			
		||||
# dashboard assets directory(only for debug mode)
 | 
			
		||||
# assets_dir = ./static
 | 
			
		||||
# console or real logFile path like ./frps.log
 | 
			
		||||
@@ -46,9 +55,38 @@ log_max_days = 3
 | 
			
		||||
# disable log colors when log_file is console, default is false
 | 
			
		||||
disable_log_color = false
 | 
			
		||||
 | 
			
		||||
# DetailedErrorsToClient defines whether to send the specific error (with debug info) to frpc. By default, this value is true.
 | 
			
		||||
detailed_errors_to_client = true
 | 
			
		||||
 | 
			
		||||
# AuthenticationMethod specifies what authentication method to use authenticate frpc with frps.
 | 
			
		||||
# If "token" is specified - token will be read into login message.
 | 
			
		||||
# If "oidc" is specified - OIDC (Open ID Connect) token will be issued using OIDC settings. By default, this value is "token".
 | 
			
		||||
authentication_method = token
 | 
			
		||||
 | 
			
		||||
# AuthenticateHeartBeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
 | 
			
		||||
authenticate_heartbeats = false
 | 
			
		||||
 | 
			
		||||
# AuthenticateNewWorkConns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
 | 
			
		||||
authenticate_new_work_conns = false
 | 
			
		||||
 | 
			
		||||
# auth token
 | 
			
		||||
token = 12345678
 | 
			
		||||
 | 
			
		||||
# OidcClientId specifies the client ID to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
 | 
			
		||||
# By default, this value is "".
 | 
			
		||||
oidc_client_id =
 | 
			
		||||
 | 
			
		||||
# OidcClientSecret specifies the client secret to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
 | 
			
		||||
# By default, this value is "".
 | 
			
		||||
oidc_client_secret = 
 | 
			
		||||
 | 
			
		||||
# OidcAudience specifies the audience of the token in OIDC authentication if AuthenticationMethod == "oidc". By default, this value is "".
 | 
			
		||||
oidc_audience = 
 | 
			
		||||
 | 
			
		||||
# OidcTokenEndpointUrl specifies the URL which implements OIDC Token Endpoint.
 | 
			
		||||
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
 | 
			
		||||
oidc_token_endpoint_url = 
 | 
			
		||||
 | 
			
		||||
# heartbeat configure, it's not recommended to modify the default value
 | 
			
		||||
# the default value of heartbeat_timeout is 90
 | 
			
		||||
# heartbeat_timeout = 90
 | 
			
		||||
@@ -62,6 +100,9 @@ max_pool_count = 5
 | 
			
		||||
# max ports can be used for each client, default value is 0 means no limit
 | 
			
		||||
max_ports_per_client = 0
 | 
			
		||||
 | 
			
		||||
# TlsOnly specifies whether to only accept TLS-encrypted connections. By default, the value is false.
 | 
			
		||||
tls_only = false
 | 
			
		||||
 | 
			
		||||
# if subdomain_host is not empty, you can set subdomain when type is http or https in frpc's configure file
 | 
			
		||||
# when subdomain is test, the host used by routing is test.frps.com
 | 
			
		||||
subdomain_host = frps.com
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
								
							@@ -4,6 +4,7 @@ go 1.12
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
 | 
			
		||||
	github.com/coreos/go-oidc v2.2.1+incompatible
 | 
			
		||||
	github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
 | 
			
		||||
	github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049
 | 
			
		||||
	github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
 | 
			
		||||
@@ -16,18 +17,21 @@ require (
 | 
			
		||||
	github.com/klauspost/reedsolomon v1.9.1 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.4 // indirect
 | 
			
		||||
	github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc
 | 
			
		||||
	github.com/pkg/errors v0.8.0 // indirect
 | 
			
		||||
	github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
 | 
			
		||||
	github.com/prometheus/client_golang v1.4.1
 | 
			
		||||
	github.com/rakyll/statik v0.1.1
 | 
			
		||||
	github.com/rodaine/table v1.0.0
 | 
			
		||||
	github.com/spf13/cobra v0.0.3
 | 
			
		||||
	github.com/spf13/pflag v1.0.1 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.3.0
 | 
			
		||||
	github.com/stretchr/testify v1.4.0
 | 
			
		||||
	github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 // indirect
 | 
			
		||||
	github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect
 | 
			
		||||
	github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 // indirect
 | 
			
		||||
	github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec
 | 
			
		||||
	github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 | 
			
		||||
	golang.org/x/text v0.3.2 // indirect
 | 
			
		||||
	golang.org/x/time v0.0.0-20191024005414-555d28b269f0
 | 
			
		||||
	gopkg.in/square/go-jose.v2 v2.4.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										100
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,51 +1,141 @@
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
			
		||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
 | 
			
		||||
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
 | 
			
		||||
github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049 h1:teH578mf2ii42NHhIp3PhgvjU5bv+NFMq9fSQR8NaG8=
 | 
			
		||||
github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049/go.mod h1:DqIrnl0rp3Zybg9zbJmozTy1n8fYJoX+QoAj9slIkKM=
 | 
			
		||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
 | 
			
		||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 | 
			
		||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
 | 
			
		||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
 | 
			
		||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
 | 
			
		||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 | 
			
		||||
github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4WFyXL8=
 | 
			
		||||
github.com/klauspost/reedsolomon v1.9.1/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8=
 | 
			
		||||
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY=
 | 
			
		||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
 | 
			
		||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 | 
			
		||||
github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8=
 | 
			
		||||
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 | 
			
		||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
 | 
			
		||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
 | 
			
		||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
 | 
			
		||||
github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
 | 
			
		||||
github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 | 
			
		||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
 | 
			
		||||
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
 | 
			
		||||
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
 | 
			
		||||
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
 | 
			
		||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
 | 
			
		||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
 | 
			
		||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
 | 
			
		||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
 | 
			
		||||
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										151
									
								
								models/auth/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								models/auth/auth.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
// Copyright 2020 guylewin, guy@lewin.co.il
 | 
			
		||||
//
 | 
			
		||||
// 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 auth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/consts"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
 | 
			
		||||
	"github.com/vaughan0/go-ini"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type baseConfig struct {
 | 
			
		||||
	// AuthenticationMethod specifies what authentication method to use to
 | 
			
		||||
	// authenticate frpc with frps. If "token" is specified - token will be
 | 
			
		||||
	// read into login message. If "oidc" is specified - OIDC (Open ID Connect)
 | 
			
		||||
	// token will be issued using OIDC settings. By default, this value is "token".
 | 
			
		||||
	AuthenticationMethod string `json:"authentication_method"`
 | 
			
		||||
	// AuthenticateHeartBeats specifies whether to include authentication token in
 | 
			
		||||
	// heartbeats sent to frps. By default, this value is false.
 | 
			
		||||
	AuthenticateHeartBeats bool `json:"authenticate_heartbeats"`
 | 
			
		||||
	// AuthenticateNewWorkConns specifies whether to include authentication token in
 | 
			
		||||
	// new work connections sent to frps. By default, this value is false.
 | 
			
		||||
	AuthenticateNewWorkConns bool `json:"authenticate_new_work_conns"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultBaseConf() baseConfig {
 | 
			
		||||
	return baseConfig{
 | 
			
		||||
		AuthenticationMethod:     "token",
 | 
			
		||||
		AuthenticateHeartBeats:   false,
 | 
			
		||||
		AuthenticateNewWorkConns: false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalBaseConfFromIni(conf ini.File) baseConfig {
 | 
			
		||||
	var (
 | 
			
		||||
		tmpStr string
 | 
			
		||||
		ok     bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	cfg := getDefaultBaseConf()
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "authentication_method"); ok {
 | 
			
		||||
		cfg.AuthenticationMethod = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "authenticate_heartbeats"); ok && tmpStr == "true" {
 | 
			
		||||
		cfg.AuthenticateHeartBeats = true
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.AuthenticateHeartBeats = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "authenticate_new_work_conns"); ok && tmpStr == "true" {
 | 
			
		||||
		cfg.AuthenticateNewWorkConns = true
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.AuthenticateNewWorkConns = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AuthClientConfig struct {
 | 
			
		||||
	baseConfig
 | 
			
		||||
	oidcClientConfig
 | 
			
		||||
	tokenConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDefaultAuthClientConf() AuthClientConfig {
 | 
			
		||||
	return AuthClientConfig{
 | 
			
		||||
		baseConfig:       getDefaultBaseConf(),
 | 
			
		||||
		oidcClientConfig: getDefaultOidcClientConf(),
 | 
			
		||||
		tokenConfig:      getDefaultTokenConf(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func UnmarshalAuthClientConfFromIni(conf ini.File) (cfg AuthClientConfig) {
 | 
			
		||||
	cfg.baseConfig = unmarshalBaseConfFromIni(conf)
 | 
			
		||||
	cfg.oidcClientConfig = unmarshalOidcClientConfFromIni(conf)
 | 
			
		||||
	cfg.tokenConfig = unmarshalTokenConfFromIni(conf)
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AuthServerConfig struct {
 | 
			
		||||
	baseConfig
 | 
			
		||||
	oidcServerConfig
 | 
			
		||||
	tokenConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDefaultAuthServerConf() AuthServerConfig {
 | 
			
		||||
	return AuthServerConfig{
 | 
			
		||||
		baseConfig:       getDefaultBaseConf(),
 | 
			
		||||
		oidcServerConfig: getDefaultOidcServerConf(),
 | 
			
		||||
		tokenConfig:      getDefaultTokenConf(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func UnmarshalAuthServerConfFromIni(conf ini.File) (cfg AuthServerConfig) {
 | 
			
		||||
	cfg.baseConfig = unmarshalBaseConfFromIni(conf)
 | 
			
		||||
	cfg.oidcServerConfig = unmarshalOidcServerConfFromIni(conf)
 | 
			
		||||
	cfg.tokenConfig = unmarshalTokenConfFromIni(conf)
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Setter interface {
 | 
			
		||||
	SetLogin(*msg.Login) error
 | 
			
		||||
	SetPing(*msg.Ping) error
 | 
			
		||||
	SetNewWorkConn(*msg.NewWorkConn) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAuthSetter(cfg AuthClientConfig) (authProvider Setter) {
 | 
			
		||||
	switch cfg.AuthenticationMethod {
 | 
			
		||||
	case consts.TokenAuthMethod:
 | 
			
		||||
		authProvider = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig)
 | 
			
		||||
	case consts.OidcAuthMethod:
 | 
			
		||||
		authProvider = NewOidcAuthSetter(cfg.baseConfig, cfg.oidcClientConfig)
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Sprintf("wrong authentication method: '%s'", cfg.AuthenticationMethod))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return authProvider
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Verifier interface {
 | 
			
		||||
	VerifyLogin(*msg.Login) error
 | 
			
		||||
	VerifyPing(*msg.Ping) error
 | 
			
		||||
	VerifyNewWorkConn(*msg.NewWorkConn) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAuthVerifier(cfg AuthServerConfig) (authVerifier Verifier) {
 | 
			
		||||
	switch cfg.AuthenticationMethod {
 | 
			
		||||
	case consts.TokenAuthMethod:
 | 
			
		||||
		authVerifier = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig)
 | 
			
		||||
	case consts.OidcAuthMethod:
 | 
			
		||||
		authVerifier = NewOidcAuthVerifier(cfg.baseConfig, cfg.oidcServerConfig)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return authVerifier
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										255
									
								
								models/auth/oidc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								models/auth/oidc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
			
		||||
// Copyright 2020 guylewin, guy@lewin.co.il
 | 
			
		||||
//
 | 
			
		||||
// 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 auth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
 | 
			
		||||
	"github.com/coreos/go-oidc"
 | 
			
		||||
	"github.com/vaughan0/go-ini"
 | 
			
		||||
	"golang.org/x/oauth2/clientcredentials"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type oidcClientConfig struct {
 | 
			
		||||
	// OidcClientId specifies the client ID to use to get a token in OIDC
 | 
			
		||||
	// authentication if AuthenticationMethod == "oidc". By default, this value
 | 
			
		||||
	// is "".
 | 
			
		||||
	OidcClientId string `json:"oidc_client_id"`
 | 
			
		||||
	// OidcClientSecret specifies the client secret to use to get a token in OIDC
 | 
			
		||||
	// authentication if AuthenticationMethod == "oidc". By default, this value
 | 
			
		||||
	// is "".
 | 
			
		||||
	OidcClientSecret string `json:"oidc_client_secret"`
 | 
			
		||||
	// OidcAudience specifies the audience of the token in OIDC authentication
 | 
			
		||||
	//if AuthenticationMethod == "oidc". By default, this value is "".
 | 
			
		||||
	OidcAudience string `json:"oidc_audience"`
 | 
			
		||||
	// OidcTokenEndpointUrl specifies the URL which implements OIDC Token Endpoint.
 | 
			
		||||
	// It will be used to get an OIDC token if AuthenticationMethod == "oidc".
 | 
			
		||||
	// By default, this value is "".
 | 
			
		||||
	OidcTokenEndpointUrl string `json:"oidc_token_endpoint_url"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultOidcClientConf() oidcClientConfig {
 | 
			
		||||
	return oidcClientConfig{
 | 
			
		||||
		OidcClientId:         "",
 | 
			
		||||
		OidcClientSecret:     "",
 | 
			
		||||
		OidcAudience:         "",
 | 
			
		||||
		OidcTokenEndpointUrl: "",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalOidcClientConfFromIni(conf ini.File) oidcClientConfig {
 | 
			
		||||
	var (
 | 
			
		||||
		tmpStr string
 | 
			
		||||
		ok     bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	cfg := getDefaultOidcClientConf()
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_client_id"); ok {
 | 
			
		||||
		cfg.OidcClientId = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_client_secret"); ok {
 | 
			
		||||
		cfg.OidcClientSecret = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_audience"); ok {
 | 
			
		||||
		cfg.OidcAudience = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_token_endpoint_url"); ok {
 | 
			
		||||
		cfg.OidcTokenEndpointUrl = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type oidcServerConfig struct {
 | 
			
		||||
	// OidcIssuer specifies the issuer to verify OIDC tokens with. This issuer
 | 
			
		||||
	// will be used to load public keys to verify signature and will be compared
 | 
			
		||||
	// with the issuer claim in the OIDC token. It will be used if
 | 
			
		||||
	// AuthenticationMethod == "oidc". By default, this value is "".
 | 
			
		||||
	OidcIssuer string `json:"oidc_issuer"`
 | 
			
		||||
	// OidcAudience specifies the audience OIDC tokens should contain when validated.
 | 
			
		||||
	// If this value is empty, audience ("client ID") verification will be skipped.
 | 
			
		||||
	// It will be used when AuthenticationMethod == "oidc". By default, this
 | 
			
		||||
	// value is "".
 | 
			
		||||
	OidcAudience string `json:"oidc_audience"`
 | 
			
		||||
	// OidcSkipExpiryCheck specifies whether to skip checking if the OIDC token is
 | 
			
		||||
	// expired. It will be used when AuthenticationMethod == "oidc". By default, this
 | 
			
		||||
	// value is false.
 | 
			
		||||
	OidcSkipExpiryCheck bool `json:"oidc_skip_expiry_check"`
 | 
			
		||||
	// OidcSkipIssuerCheck specifies whether to skip checking if the OIDC token's
 | 
			
		||||
	// issuer claim matches the issuer specified in OidcIssuer. It will be used when
 | 
			
		||||
	// AuthenticationMethod == "oidc". By default, this value is false.
 | 
			
		||||
	OidcSkipIssuerCheck bool `json:"oidc_skip_issuer_check"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultOidcServerConf() oidcServerConfig {
 | 
			
		||||
	return oidcServerConfig{
 | 
			
		||||
		OidcIssuer:          "",
 | 
			
		||||
		OidcAudience:        "",
 | 
			
		||||
		OidcSkipExpiryCheck: false,
 | 
			
		||||
		OidcSkipIssuerCheck: false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalOidcServerConfFromIni(conf ini.File) oidcServerConfig {
 | 
			
		||||
	var (
 | 
			
		||||
		tmpStr string
 | 
			
		||||
		ok     bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	cfg := getDefaultOidcServerConf()
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_issuer"); ok {
 | 
			
		||||
		cfg.OidcIssuer = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_audience"); ok {
 | 
			
		||||
		cfg.OidcAudience = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_skip_expiry_check"); ok && tmpStr == "true" {
 | 
			
		||||
		cfg.OidcSkipExpiryCheck = true
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.OidcSkipExpiryCheck = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "oidc_skip_issuer_check"); ok && tmpStr == "true" {
 | 
			
		||||
		cfg.OidcSkipIssuerCheck = true
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.OidcSkipIssuerCheck = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OidcAuthProvider struct {
 | 
			
		||||
	baseConfig
 | 
			
		||||
 | 
			
		||||
	tokenGenerator *clientcredentials.Config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewOidcAuthSetter(baseCfg baseConfig, cfg oidcClientConfig) *OidcAuthProvider {
 | 
			
		||||
	tokenGenerator := &clientcredentials.Config{
 | 
			
		||||
		ClientID:     cfg.OidcClientId,
 | 
			
		||||
		ClientSecret: cfg.OidcClientSecret,
 | 
			
		||||
		Scopes:       []string{cfg.OidcAudience},
 | 
			
		||||
		TokenURL:     cfg.OidcTokenEndpointUrl,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &OidcAuthProvider{
 | 
			
		||||
		baseConfig:     baseCfg,
 | 
			
		||||
		tokenGenerator: tokenGenerator,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
 | 
			
		||||
	tokenObj, err := auth.tokenGenerator.Token(context.Background())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return tokenObj.AccessToken, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthProvider) SetLogin(loginMsg *msg.Login) (err error) {
 | 
			
		||||
	loginMsg.PrivilegeKey, err = auth.generateAccessToken()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) {
 | 
			
		||||
	if !auth.AuthenticateHeartBeats {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pingMsg.PrivilegeKey, err = auth.generateAccessToken()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
 | 
			
		||||
	if !auth.AuthenticateNewWorkConns {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newWorkConnMsg.PrivilegeKey, err = auth.generateAccessToken()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OidcAuthConsumer struct {
 | 
			
		||||
	baseConfig
 | 
			
		||||
 | 
			
		||||
	verifier         *oidc.IDTokenVerifier
 | 
			
		||||
	subjectFromLogin string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewOidcAuthVerifier(baseCfg baseConfig, cfg oidcServerConfig) *OidcAuthConsumer {
 | 
			
		||||
	provider, err := oidc.NewProvider(context.Background(), cfg.OidcIssuer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	verifierConf := oidc.Config{
 | 
			
		||||
		ClientID:          cfg.OidcAudience,
 | 
			
		||||
		SkipClientIDCheck: cfg.OidcAudience == "",
 | 
			
		||||
		SkipExpiryCheck:   cfg.OidcSkipExpiryCheck,
 | 
			
		||||
		SkipIssuerCheck:   cfg.OidcSkipIssuerCheck,
 | 
			
		||||
	}
 | 
			
		||||
	return &OidcAuthConsumer{
 | 
			
		||||
		baseConfig: baseCfg,
 | 
			
		||||
		verifier:   provider.Verifier(&verifierConf),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {
 | 
			
		||||
	token, err := auth.verifier.Verify(context.Background(), loginMsg.PrivilegeKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("invalid OIDC token in login: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	auth.subjectFromLogin = token.Subject
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err error) {
 | 
			
		||||
	token, err := auth.verifier.Verify(context.Background(), privilegeKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("invalid OIDC token in ping: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if token.Subject != auth.subjectFromLogin {
 | 
			
		||||
		return fmt.Errorf("received different OIDC subject in login and ping. "+
 | 
			
		||||
			"original subject: %s, "+
 | 
			
		||||
			"new subject: %s",
 | 
			
		||||
			auth.subjectFromLogin, token.Subject)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
 | 
			
		||||
	if !auth.AuthenticateHeartBeats {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return auth.verifyPostLoginToken(pingMsg.PrivilegeKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *OidcAuthConsumer) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
 | 
			
		||||
	if !auth.AuthenticateNewWorkConns {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return auth.verifyPostLoginToken(newWorkConnMsg.PrivilegeKey)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										120
									
								
								models/auth/token.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								models/auth/token.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
// Copyright 2020 guylewin, guy@lewin.co.il
 | 
			
		||||
//
 | 
			
		||||
// 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 auth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
 | 
			
		||||
	"github.com/vaughan0/go-ini"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type tokenConfig struct {
 | 
			
		||||
	// Token specifies the authorization token used to create keys to be sent
 | 
			
		||||
	// to the server. The server must have a matching token for authorization
 | 
			
		||||
	// to succeed.  By default, this value is "".
 | 
			
		||||
	Token string `json:"token"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultTokenConf() tokenConfig {
 | 
			
		||||
	return tokenConfig{
 | 
			
		||||
		Token: "",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalTokenConfFromIni(conf ini.File) tokenConfig {
 | 
			
		||||
	var (
 | 
			
		||||
		tmpStr string
 | 
			
		||||
		ok     bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	cfg := getDefaultTokenConf()
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "token"); ok {
 | 
			
		||||
		cfg.Token = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TokenAuthSetterVerifier struct {
 | 
			
		||||
	baseConfig
 | 
			
		||||
 | 
			
		||||
	token string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewTokenAuth(baseCfg baseConfig, cfg tokenConfig) *TokenAuthSetterVerifier {
 | 
			
		||||
	return &TokenAuthSetterVerifier{
 | 
			
		||||
		baseConfig: baseCfg,
 | 
			
		||||
		token:      cfg.Token,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *TokenAuthSetterVerifier) SetLogin(loginMsg *msg.Login) (err error) {
 | 
			
		||||
	loginMsg.PrivilegeKey = util.GetAuthKey(auth.token, loginMsg.Timestamp)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *TokenAuthSetterVerifier) SetPing(pingMsg *msg.Ping) error {
 | 
			
		||||
	if !auth.AuthenticateHeartBeats {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pingMsg.Timestamp = time.Now().Unix()
 | 
			
		||||
	pingMsg.PrivilegeKey = util.GetAuthKey(auth.token, pingMsg.Timestamp)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *TokenAuthSetterVerifier) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) error {
 | 
			
		||||
	if !auth.AuthenticateHeartBeats {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newWorkConnMsg.Timestamp = time.Now().Unix()
 | 
			
		||||
	newWorkConnMsg.PrivilegeKey = util.GetAuthKey(auth.token, newWorkConnMsg.Timestamp)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *TokenAuthSetterVerifier) VerifyLogin(loginMsg *msg.Login) error {
 | 
			
		||||
	if util.GetAuthKey(auth.token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
 | 
			
		||||
		return fmt.Errorf("token in login doesn't match token from configuration")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *TokenAuthSetterVerifier) VerifyPing(pingMsg *msg.Ping) error {
 | 
			
		||||
	if !auth.AuthenticateHeartBeats {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if util.GetAuthKey(auth.token, pingMsg.Timestamp) != pingMsg.PrivilegeKey {
 | 
			
		||||
		return fmt.Errorf("token in heartbeat doesn't match token from configuration")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *TokenAuthSetterVerifier) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) error {
 | 
			
		||||
	if !auth.AuthenticateNewWorkConns {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if util.GetAuthKey(auth.token, newWorkConnMsg.Timestamp) != newWorkConnMsg.PrivilegeKey {
 | 
			
		||||
		return fmt.Errorf("token in NewWorkConn doesn't match token from configuration")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -21,12 +21,15 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	ini "github.com/vaughan0/go-ini"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ClientCommonConf contains information for a client service. It is
 | 
			
		||||
// recommended to use GetDefaultClientConf instead of creating this object
 | 
			
		||||
// directly, so that all unspecified fields have reasonable default values.
 | 
			
		||||
type ClientCommonConf struct {
 | 
			
		||||
	auth.AuthClientConfig
 | 
			
		||||
	// ServerAddr specifies the address of the server to connect to. By
 | 
			
		||||
	// default, this value is "0.0.0.0".
 | 
			
		||||
	ServerAddr string `json:"server_addr"`
 | 
			
		||||
@@ -56,10 +59,6 @@ type ClientCommonConf struct {
 | 
			
		||||
	// DisableLogColor disables log colors when LogWay == "console" when set to
 | 
			
		||||
	// true. By default, this value is false.
 | 
			
		||||
	DisableLogColor bool `json:"disable_log_color"`
 | 
			
		||||
	// Token specifies the authorization token used to create keys to be sent
 | 
			
		||||
	// to the server. The server must have a matching token for authorization
 | 
			
		||||
	// to succeed.  By default, this value is "".
 | 
			
		||||
	Token string `json:"token"`
 | 
			
		||||
	// AdminAddr specifies the address that the admin server binds to. By
 | 
			
		||||
	// default, this value is "127.0.0.1".
 | 
			
		||||
	AdminAddr string `json:"admin_addr"`
 | 
			
		||||
@@ -130,7 +129,6 @@ func GetDefaultClientConf() ClientCommonConf {
 | 
			
		||||
		LogLevel:          "info",
 | 
			
		||||
		LogMaxDays:        3,
 | 
			
		||||
		DisableLogColor:   false,
 | 
			
		||||
		Token:             "",
 | 
			
		||||
		AdminAddr:         "127.0.0.1",
 | 
			
		||||
		AdminPort:         0,
 | 
			
		||||
		AdminUser:         "",
 | 
			
		||||
@@ -158,6 +156,8 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error
 | 
			
		||||
		return ClientCommonConf{}, fmt.Errorf("parse ini conf file error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg.AuthClientConfig = auth.UnmarshalAuthClientConfFromIni(conf)
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		tmpStr string
 | 
			
		||||
		ok     bool
 | 
			
		||||
@@ -203,10 +203,6 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "token"); ok {
 | 
			
		||||
		cfg.Token = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
 | 
			
		||||
		cfg.AdminAddr = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ var (
 | 
			
		||||
func init() {
 | 
			
		||||
	proxyConfTypeMap = make(map[string]reflect.Type)
 | 
			
		||||
	proxyConfTypeMap[consts.TcpProxy] = reflect.TypeOf(TcpProxyConf{})
 | 
			
		||||
	proxyConfTypeMap[consts.TcpMuxProxy] = reflect.TypeOf(TcpMuxProxyConf{})
 | 
			
		||||
	proxyConfTypeMap[consts.UdpProxy] = reflect.TypeOf(UdpProxyConf{})
 | 
			
		||||
	proxyConfTypeMap[consts.HttpProxy] = reflect.TypeOf(HttpProxyConf{})
 | 
			
		||||
	proxyConfTypeMap[consts.HttpsProxy] = reflect.TypeOf(HttpsProxyConf{})
 | 
			
		||||
@@ -149,7 +150,7 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool {
 | 
			
		||||
		cfg.Group != cmp.Group ||
 | 
			
		||||
		cfg.GroupKey != cmp.GroupKey ||
 | 
			
		||||
		cfg.ProxyProtocolVersion != cmp.ProxyProtocolVersion ||
 | 
			
		||||
		cfg.BandwidthLimit.Equal(&cmp.BandwidthLimit) ||
 | 
			
		||||
		!cfg.BandwidthLimit.Equal(&cmp.BandwidthLimit) ||
 | 
			
		||||
		!reflect.DeepEqual(cfg.Metas, cmp.Metas) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
@@ -574,6 +575,84 @@ func (cfg *TcpProxyConf) CheckForCli() (err error) {
 | 
			
		||||
 | 
			
		||||
func (cfg *TcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil }
 | 
			
		||||
 | 
			
		||||
// TCP Multiplexer
 | 
			
		||||
type TcpMuxProxyConf struct {
 | 
			
		||||
	BaseProxyConf
 | 
			
		||||
	DomainConf
 | 
			
		||||
 | 
			
		||||
	Multiplexer string `json:"multiplexer"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *TcpMuxProxyConf) Compare(cmp ProxyConf) bool {
 | 
			
		||||
	cmpConf, ok := cmp.(*TcpMuxProxyConf)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
 | 
			
		||||
		!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
 | 
			
		||||
		cfg.Multiplexer != cmpConf.Multiplexer {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *TcpMuxProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
 | 
			
		||||
	cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
 | 
			
		||||
	cfg.DomainConf.UnmarshalFromMsg(pMsg)
 | 
			
		||||
	cfg.Multiplexer = pMsg.Multiplexer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *TcpMuxProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
 | 
			
		||||
	if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg.Multiplexer = section["multiplexer"]
 | 
			
		||||
	if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer {
 | 
			
		||||
		return fmt.Errorf("parse conf error: proxy [%s] incorrect multiplexer [%s]", name, cfg.Multiplexer)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *TcpMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
 | 
			
		||||
	cfg.BaseProxyConf.MarshalToMsg(pMsg)
 | 
			
		||||
	cfg.DomainConf.MarshalToMsg(pMsg)
 | 
			
		||||
	pMsg.Multiplexer = cfg.Multiplexer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *TcpMuxProxyConf) CheckForCli() (err error) {
 | 
			
		||||
	if err = cfg.BaseProxyConf.checkForCli(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = cfg.DomainConf.checkForCli(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer {
 | 
			
		||||
		return fmt.Errorf("parse conf error: incorrect multiplexer [%s]", cfg.Multiplexer)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *TcpMuxProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
 | 
			
		||||
	if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer {
 | 
			
		||||
		return fmt.Errorf("proxy [%s] incorrect multiplexer [%s]", cfg.ProxyName, cfg.Multiplexer)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cfg.Multiplexer == consts.HttpConnectTcpMultiplexer && serverCfg.TcpMuxHttpConnectPort == 0 {
 | 
			
		||||
		return fmt.Errorf("proxy [%s] type [tcpmux] with multiplexer [httpconnect] requires tcpmux_httpconnect_port configuration", cfg.ProxyName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = cfg.DomainConf.checkForSvr(serverCfg); err != nil {
 | 
			
		||||
		err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UDP
 | 
			
		||||
type UdpProxyConf struct {
 | 
			
		||||
	BaseProxyConf
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	ini "github.com/vaughan0/go-ini"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
	plugin "github.com/fatedier/frp/models/plugin/server"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
)
 | 
			
		||||
@@ -29,6 +30,7 @@ import (
 | 
			
		||||
// recommended to use GetDefaultServerConf instead of creating this object
 | 
			
		||||
// directly, so that all unspecified fields have reasonable default values.
 | 
			
		||||
type ServerCommonConf struct {
 | 
			
		||||
	auth.AuthServerConfig
 | 
			
		||||
	// BindAddr specifies the address that the server binds to. By default,
 | 
			
		||||
	// this value is "0.0.0.0".
 | 
			
		||||
	BindAddr string `json:"bind_addr"`
 | 
			
		||||
@@ -46,25 +48,25 @@ type ServerCommonConf struct {
 | 
			
		||||
	// ProxyBindAddr specifies the address that the proxy binds to. This value
 | 
			
		||||
	// may be the same as BindAddr. By default, this value is "0.0.0.0".
 | 
			
		||||
	ProxyBindAddr string `json:"proxy_bind_addr"`
 | 
			
		||||
 | 
			
		||||
	// VhostHttpPort specifies the port that the server listens for HTTP Vhost
 | 
			
		||||
	// requests. If this value is 0, the server will not listen for HTTP
 | 
			
		||||
	// requests. By default, this value is 0.
 | 
			
		||||
	VhostHttpPort int `json:"vhost_http_port"`
 | 
			
		||||
 | 
			
		||||
	// VhostHttpsPort specifies the port that the server listens for HTTPS
 | 
			
		||||
	// Vhost requests. If this value is 0, the server will not listen for HTTPS
 | 
			
		||||
	// requests. By default, this value is 0.
 | 
			
		||||
	VhostHttpsPort int `json:"vhost_https_port"`
 | 
			
		||||
 | 
			
		||||
	// TcpMuxHttpConnectPort specifies the port that the server listens for TCP
 | 
			
		||||
	// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
 | 
			
		||||
	// requests on one single port. If it's not - it will listen on this value for
 | 
			
		||||
	// HTTP CONNECT requests. By default, this value is 0.
 | 
			
		||||
	TcpMuxHttpConnectPort int `json:"tcpmux_httpconnect_port"`
 | 
			
		||||
	// VhostHttpTimeout specifies the response header timeout for the Vhost
 | 
			
		||||
	// HTTP server, in seconds. By default, this value is 60.
 | 
			
		||||
	VhostHttpTimeout int64 `json:"vhost_http_timeout"`
 | 
			
		||||
 | 
			
		||||
	// DashboardAddr specifies the address that the dashboard binds to. By
 | 
			
		||||
	// default, this value is "0.0.0.0".
 | 
			
		||||
	DashboardAddr string `json:"dashboard_addr"`
 | 
			
		||||
 | 
			
		||||
	// DashboardPort specifies the port that the dashboard listens on. If this
 | 
			
		||||
	// value is 0, the dashboard will not be started. By default, this value is
 | 
			
		||||
	// 0.
 | 
			
		||||
@@ -75,6 +77,9 @@ type ServerCommonConf struct {
 | 
			
		||||
	// DashboardUser specifies the password that the dashboard will use for
 | 
			
		||||
	// login. By default, this value is "admin".
 | 
			
		||||
	DashboardPwd string `json:"dashboard_pwd"`
 | 
			
		||||
	// EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port}
 | 
			
		||||
	// in /metrics api.
 | 
			
		||||
	EnablePrometheus bool `json:"enable_prometheus"`
 | 
			
		||||
	// AssetsDir specifies the local directory that the dashboard will load
 | 
			
		||||
	// resources from. If this value is "", assets will be loaded from the
 | 
			
		||||
	// bundled executable using statik. By default, this value is "".
 | 
			
		||||
@@ -98,10 +103,10 @@ type ServerCommonConf struct {
 | 
			
		||||
	// DisableLogColor disables log colors when LogWay == "console" when set to
 | 
			
		||||
	// true. By default, this value is false.
 | 
			
		||||
	DisableLogColor bool `json:"disable_log_color"`
 | 
			
		||||
	// Token specifies the authorization token used to authenticate keys
 | 
			
		||||
	// received from clients. Clients must have a matching token to be
 | 
			
		||||
	// authorized to use the server. By default, this value is "".
 | 
			
		||||
	Token string `json:"token"`
 | 
			
		||||
	// DetailedErrorsToClient defines whether to send the specific error (with
 | 
			
		||||
	// debug info) to frpc. By default, this value is true.
 | 
			
		||||
	DetailedErrorsToClient bool `json:"detailed_errors_to_client"`
 | 
			
		||||
 | 
			
		||||
	// SubDomainHost specifies the domain that will be attached to sub-domains
 | 
			
		||||
	// requested by the client when using Vhost proxying. For example, if this
 | 
			
		||||
	// value is set to "frps.com" and the client requested the subdomain
 | 
			
		||||
@@ -128,6 +133,9 @@ type ServerCommonConf struct {
 | 
			
		||||
	// may proxy to. If this value is 0, no limit will be applied. By default,
 | 
			
		||||
	// this value is 0.
 | 
			
		||||
	MaxPortsPerClient int64 `json:"max_ports_per_client"`
 | 
			
		||||
	// TlsOnly specifies whether to only accept TLS-encrypted connections. By
 | 
			
		||||
	// default, the value is false.
 | 
			
		||||
	TlsOnly bool `json:"tls_only"`
 | 
			
		||||
	// HeartBeatTimeout specifies the maximum time to wait for a heartbeat
 | 
			
		||||
	// before terminating the connection. It is not recommended to change this
 | 
			
		||||
	// value. By default, this value is 90.
 | 
			
		||||
@@ -143,34 +151,37 @@ type ServerCommonConf struct {
 | 
			
		||||
// defaults.
 | 
			
		||||
func GetDefaultServerConf() ServerCommonConf {
 | 
			
		||||
	return ServerCommonConf{
 | 
			
		||||
		BindAddr:          "0.0.0.0",
 | 
			
		||||
		BindPort:          7000,
 | 
			
		||||
		BindUdpPort:       0,
 | 
			
		||||
		KcpBindPort:       0,
 | 
			
		||||
		ProxyBindAddr:     "0.0.0.0",
 | 
			
		||||
		VhostHttpPort:     0,
 | 
			
		||||
		VhostHttpsPort:    0,
 | 
			
		||||
		VhostHttpTimeout:  60,
 | 
			
		||||
		DashboardAddr:     "0.0.0.0",
 | 
			
		||||
		DashboardPort:     0,
 | 
			
		||||
		DashboardUser:     "admin",
 | 
			
		||||
		DashboardPwd:      "admin",
 | 
			
		||||
		AssetsDir:         "",
 | 
			
		||||
		LogFile:           "console",
 | 
			
		||||
		LogWay:            "console",
 | 
			
		||||
		LogLevel:          "info",
 | 
			
		||||
		LogMaxDays:        3,
 | 
			
		||||
		DisableLogColor:   false,
 | 
			
		||||
		Token:             "",
 | 
			
		||||
		SubDomainHost:     "",
 | 
			
		||||
		TcpMux:            true,
 | 
			
		||||
		AllowPorts:        make(map[int]struct{}),
 | 
			
		||||
		MaxPoolCount:      5,
 | 
			
		||||
		MaxPortsPerClient: 0,
 | 
			
		||||
		HeartBeatTimeout:  90,
 | 
			
		||||
		UserConnTimeout:   10,
 | 
			
		||||
		Custom404Page:     "",
 | 
			
		||||
		HTTPPlugins:       make(map[string]plugin.HTTPPluginOptions),
 | 
			
		||||
		BindAddr:               "0.0.0.0",
 | 
			
		||||
		BindPort:               7000,
 | 
			
		||||
		BindUdpPort:            0,
 | 
			
		||||
		KcpBindPort:            0,
 | 
			
		||||
		ProxyBindAddr:          "0.0.0.0",
 | 
			
		||||
		VhostHttpPort:          0,
 | 
			
		||||
		VhostHttpsPort:         0,
 | 
			
		||||
		TcpMuxHttpConnectPort:  0,
 | 
			
		||||
		VhostHttpTimeout:       60,
 | 
			
		||||
		DashboardAddr:          "0.0.0.0",
 | 
			
		||||
		DashboardPort:          0,
 | 
			
		||||
		DashboardUser:          "admin",
 | 
			
		||||
		DashboardPwd:           "admin",
 | 
			
		||||
		EnablePrometheus:       false,
 | 
			
		||||
		AssetsDir:              "",
 | 
			
		||||
		LogFile:                "console",
 | 
			
		||||
		LogWay:                 "console",
 | 
			
		||||
		LogLevel:               "info",
 | 
			
		||||
		LogMaxDays:             3,
 | 
			
		||||
		DisableLogColor:        false,
 | 
			
		||||
		DetailedErrorsToClient: true,
 | 
			
		||||
		SubDomainHost:          "",
 | 
			
		||||
		TcpMux:                 true,
 | 
			
		||||
		AllowPorts:             make(map[int]struct{}),
 | 
			
		||||
		MaxPoolCount:           5,
 | 
			
		||||
		MaxPortsPerClient:      0,
 | 
			
		||||
		TlsOnly:                false,
 | 
			
		||||
		HeartBeatTimeout:       90,
 | 
			
		||||
		UserConnTimeout:        10,
 | 
			
		||||
		Custom404Page:          "",
 | 
			
		||||
		HTTPPlugins:            make(map[string]plugin.HTTPPluginOptions),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -187,6 +198,8 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
 | 
			
		||||
 | 
			
		||||
	UnmarshalPluginsFromIni(conf, &cfg)
 | 
			
		||||
 | 
			
		||||
	cfg.AuthServerConfig = auth.UnmarshalAuthServerConfFromIni(conf)
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		tmpStr string
 | 
			
		||||
		ok     bool
 | 
			
		||||
@@ -251,6 +264,17 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
 | 
			
		||||
		cfg.VhostHttpsPort = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "tcpmux_httpconnect_port"); ok {
 | 
			
		||||
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 | 
			
		||||
			err = fmt.Errorf("Parse conf error: invalid tcpmux_httpconnect_port")
 | 
			
		||||
			return
 | 
			
		||||
		} else {
 | 
			
		||||
			cfg.TcpMuxHttpConnectPort = int(v)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.TcpMuxHttpConnectPort = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "vhost_http_timeout"); ok {
 | 
			
		||||
		v, errRet := strconv.ParseInt(tmpStr, 10, 64)
 | 
			
		||||
		if errRet != nil || v < 0 {
 | 
			
		||||
@@ -286,6 +310,10 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
 | 
			
		||||
		cfg.DashboardPwd = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "enable_prometheus"); ok && tmpStr == "true" {
 | 
			
		||||
		cfg.EnablePrometheus = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
 | 
			
		||||
		cfg.AssetsDir = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
@@ -314,7 +342,11 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
 | 
			
		||||
		cfg.DisableLogColor = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg.Token, _ = conf.Get("common", "token")
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "detailed_errors_to_client"); ok && tmpStr == "false" {
 | 
			
		||||
		cfg.DetailedErrorsToClient = false
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.DetailedErrorsToClient = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {
 | 
			
		||||
		// e.g. 1000-2000,2001,2002,3000-4000
 | 
			
		||||
@@ -378,6 +410,12 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
 | 
			
		||||
			cfg.HeartBeatTimeout = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "tls_only"); ok && tmpStr == "true" {
 | 
			
		||||
		cfg.TlsOnly = true
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.TlsOnly = false
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,10 +23,18 @@ var (
 | 
			
		||||
	Offline string = "offline"
 | 
			
		||||
 | 
			
		||||
	// proxy type
 | 
			
		||||
	TcpProxy   string = "tcp"
 | 
			
		||||
	UdpProxy   string = "udp"
 | 
			
		||||
	HttpProxy  string = "http"
 | 
			
		||||
	HttpsProxy string = "https"
 | 
			
		||||
	StcpProxy  string = "stcp"
 | 
			
		||||
	XtcpProxy  string = "xtcp"
 | 
			
		||||
	TcpProxy    string = "tcp"
 | 
			
		||||
	UdpProxy    string = "udp"
 | 
			
		||||
	TcpMuxProxy string = "tcpmux"
 | 
			
		||||
	HttpProxy   string = "http"
 | 
			
		||||
	HttpsProxy  string = "https"
 | 
			
		||||
	StcpProxy   string = "stcp"
 | 
			
		||||
	XtcpProxy   string = "xtcp"
 | 
			
		||||
 | 
			
		||||
	// authentication method
 | 
			
		||||
	TokenAuthMethod string = "token"
 | 
			
		||||
	OidcAuthMethod  string = "oidc"
 | 
			
		||||
 | 
			
		||||
	// tcp multiplexer
 | 
			
		||||
	HttpConnectTcpMultiplexer string = "httpconnect"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										93
									
								
								models/metrics/aggregate/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								models/metrics/aggregate/server.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
// Copyright 2020 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 aggregate
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/fatedier/frp/models/metrics/mem"
 | 
			
		||||
	"github.com/fatedier/frp/models/metrics/prometheus"
 | 
			
		||||
	"github.com/fatedier/frp/server/metrics"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EnableMem start to mark metrics to memory monitor system.
 | 
			
		||||
func EnableMem() {
 | 
			
		||||
	sm.Add(mem.ServerMetrics)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnablePrometheus start to mark metrics to prometheus.
 | 
			
		||||
func EnablePrometheus() {
 | 
			
		||||
	sm.Add(prometheus.ServerMetrics)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var sm *serverMetrics = &serverMetrics{}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	metrics.Register(sm)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type serverMetrics struct {
 | 
			
		||||
	ms []metrics.ServerMetrics
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) Add(sm metrics.ServerMetrics) {
 | 
			
		||||
	m.ms = append(m.ms, sm)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) NewClient() {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.NewClient()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseClient() {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.CloseClient()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) NewProxy(name string, proxyType string) {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.NewProxy(name, proxyType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseProxy(name string, proxyType string) {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.CloseProxy(name, proxyType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.OpenConnection(name, proxyType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseConnection(name string, proxyType string) {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.CloseConnection(name, proxyType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.AddTrafficIn(name, proxyType, trafficBytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
 | 
			
		||||
	for _, v := range m.ms {
 | 
			
		||||
		v.AddTrafficOut(name, proxyType, trafficBytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										262
									
								
								models/metrics/mem/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								models/metrics/mem/server.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
// Copyright 2019 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 mem
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	server "github.com/fatedier/frp/server/metrics"
 | 
			
		||||
	"github.com/fatedier/frp/utils/log"
 | 
			
		||||
	"github.com/fatedier/frp/utils/metric"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var sm *serverMetrics = newServerMetrics()
 | 
			
		||||
var ServerMetrics server.ServerMetrics
 | 
			
		||||
var StatsCollector Collector
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	ServerMetrics = sm
 | 
			
		||||
	StatsCollector = sm
 | 
			
		||||
	sm.run()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type serverMetrics struct {
 | 
			
		||||
	info *ServerStatistics
 | 
			
		||||
	mu   sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newServerMetrics() *serverMetrics {
 | 
			
		||||
	return &serverMetrics{
 | 
			
		||||
		info: &ServerStatistics{
 | 
			
		||||
			TotalTrafficIn:  metric.NewDateCounter(ReserveDays),
 | 
			
		||||
			TotalTrafficOut: metric.NewDateCounter(ReserveDays),
 | 
			
		||||
			CurConns:        metric.NewCounter(),
 | 
			
		||||
 | 
			
		||||
			ClientCounts:    metric.NewCounter(),
 | 
			
		||||
			ProxyTypeCounts: make(map[string]metric.Counter),
 | 
			
		||||
 | 
			
		||||
			ProxyStatistics: make(map[string]*ProxyStatistics),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) run() {
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			time.Sleep(12 * time.Hour)
 | 
			
		||||
			log.Debug("start to clear useless proxy statistics data...")
 | 
			
		||||
			m.clearUselessInfo()
 | 
			
		||||
			log.Debug("finish to clear useless proxy statistics data")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) clearUselessInfo() {
 | 
			
		||||
	// To check if there are proxies that closed than 7 days and drop them.
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
	for name, data := range m.info.ProxyStatistics {
 | 
			
		||||
		if !data.LastCloseTime.IsZero() && time.Since(data.LastCloseTime) > time.Duration(7*24)*time.Hour {
 | 
			
		||||
			delete(m.info.ProxyStatistics, name)
 | 
			
		||||
			log.Trace("clear proxy [%s]'s statistics data, lastCloseTime: [%s]", name, data.LastCloseTime.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) NewClient() {
 | 
			
		||||
	m.info.ClientCounts.Inc(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseClient() {
 | 
			
		||||
	m.info.ClientCounts.Dec(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) NewProxy(name string, proxyType string) {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
	counter, ok := m.info.ProxyTypeCounts[proxyType]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		counter = metric.NewCounter()
 | 
			
		||||
	}
 | 
			
		||||
	counter.Inc(1)
 | 
			
		||||
	m.info.ProxyTypeCounts[proxyType] = counter
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := m.info.ProxyStatistics[name]
 | 
			
		||||
	if !(ok && proxyStats.ProxyType == proxyType) {
 | 
			
		||||
		proxyStats = &ProxyStatistics{
 | 
			
		||||
			Name:       name,
 | 
			
		||||
			ProxyType:  proxyType,
 | 
			
		||||
			CurConns:   metric.NewCounter(),
 | 
			
		||||
			TrafficIn:  metric.NewDateCounter(ReserveDays),
 | 
			
		||||
			TrafficOut: metric.NewDateCounter(ReserveDays),
 | 
			
		||||
		}
 | 
			
		||||
		m.info.ProxyStatistics[name] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
	proxyStats.LastStartTime = time.Now()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseProxy(name string, proxyType string) {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
	if counter, ok := m.info.ProxyTypeCounts[proxyType]; ok {
 | 
			
		||||
		counter.Dec(1)
 | 
			
		||||
	}
 | 
			
		||||
	if proxyStats, ok := m.info.ProxyStatistics[name]; ok {
 | 
			
		||||
		proxyStats.LastCloseTime = time.Now()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
 | 
			
		||||
	m.info.CurConns.Inc(1)
 | 
			
		||||
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
	proxyStats, ok := m.info.ProxyStatistics[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.CurConns.Inc(1)
 | 
			
		||||
		m.info.ProxyStatistics[name] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseConnection(name string, proxyType string) {
 | 
			
		||||
	m.info.CurConns.Dec(1)
 | 
			
		||||
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
	proxyStats, ok := m.info.ProxyStatistics[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.CurConns.Dec(1)
 | 
			
		||||
		m.info.ProxyStatistics[name] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
 | 
			
		||||
	m.info.TotalTrafficIn.Inc(trafficBytes)
 | 
			
		||||
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := m.info.ProxyStatistics[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.TrafficIn.Inc(trafficBytes)
 | 
			
		||||
		m.info.ProxyStatistics[name] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
 | 
			
		||||
	m.info.TotalTrafficOut.Inc(trafficBytes)
 | 
			
		||||
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := m.info.ProxyStatistics[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.TrafficOut.Inc(trafficBytes)
 | 
			
		||||
		m.info.ProxyStatistics[name] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get stats data api.
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) GetServer() *ServerStats {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
	s := &ServerStats{
 | 
			
		||||
		TotalTrafficIn:  m.info.TotalTrafficIn.TodayCount(),
 | 
			
		||||
		TotalTrafficOut: m.info.TotalTrafficOut.TodayCount(),
 | 
			
		||||
		CurConns:        m.info.CurConns.Count(),
 | 
			
		||||
		ClientCounts:    m.info.ClientCounts.Count(),
 | 
			
		||||
		ProxyTypeCounts: make(map[string]int64),
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range m.info.ProxyTypeCounts {
 | 
			
		||||
		s.ProxyTypeCounts[k] = v.Count()
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) GetProxiesByType(proxyType string) []*ProxyStats {
 | 
			
		||||
	res := make([]*ProxyStats, 0)
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	for name, proxyStats := range m.info.ProxyStatistics {
 | 
			
		||||
		if proxyStats.ProxyType != proxyType {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ps := &ProxyStats{
 | 
			
		||||
			Name:            name,
 | 
			
		||||
			Type:            proxyStats.ProxyType,
 | 
			
		||||
			TodayTrafficIn:  proxyStats.TrafficIn.TodayCount(),
 | 
			
		||||
			TodayTrafficOut: proxyStats.TrafficOut.TodayCount(),
 | 
			
		||||
			CurConns:        proxyStats.CurConns.Count(),
 | 
			
		||||
		}
 | 
			
		||||
		if !proxyStats.LastStartTime.IsZero() {
 | 
			
		||||
			ps.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05")
 | 
			
		||||
		}
 | 
			
		||||
		if !proxyStats.LastCloseTime.IsZero() {
 | 
			
		||||
			ps.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05")
 | 
			
		||||
		}
 | 
			
		||||
		res = append(res, ps)
 | 
			
		||||
	}
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) GetProxiesByTypeAndName(proxyType string, proxyName string) (res *ProxyStats) {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	for name, proxyStats := range m.info.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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) GetProxyTraffic(name string) (res *ProxyTrafficInfo) {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := m.info.ProxyStatistics[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		res = &ProxyTrafficInfo{
 | 
			
		||||
			Name: name,
 | 
			
		||||
		}
 | 
			
		||||
		res.TrafficIn = proxyStats.TrafficIn.GetLastDaysCount(ReserveDays)
 | 
			
		||||
		res.TrafficOut = proxyStats.TrafficOut.GetLastDaysCount(ReserveDays)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package stats
 | 
			
		||||
package mem
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -24,19 +24,6 @@ const (
 | 
			
		||||
	ReserveDays = 7
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StatsType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	TypeNewClient StatsType = iota
 | 
			
		||||
	TypeCloseClient
 | 
			
		||||
	TypeNewProxy
 | 
			
		||||
	TypeCloseProxy
 | 
			
		||||
	TypeOpenConnection
 | 
			
		||||
	TypeCloseConnection
 | 
			
		||||
	TypeAddTrafficIn
 | 
			
		||||
	TypeAddTrafficOut
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ServerStats struct {
 | 
			
		||||
	TotalTrafficIn  int64
 | 
			
		||||
	TotalTrafficOut int64
 | 
			
		||||
@@ -88,42 +75,8 @@ type ServerStatistics struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Collector interface {
 | 
			
		||||
	Mark(statsType StatsType, payload interface{})
 | 
			
		||||
	Run() error
 | 
			
		||||
	GetServer() *ServerStats
 | 
			
		||||
	GetProxiesByType(proxyType string) []*ProxyStats
 | 
			
		||||
	GetProxiesByTypeAndName(proxyType string, proxyName string) *ProxyStats
 | 
			
		||||
	GetProxyTraffic(name string) *ProxyTrafficInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NewClientPayload struct{}
 | 
			
		||||
 | 
			
		||||
type CloseClientPayload struct{}
 | 
			
		||||
 | 
			
		||||
type NewProxyPayload struct {
 | 
			
		||||
	Name      string
 | 
			
		||||
	ProxyType string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CloseProxyPayload struct {
 | 
			
		||||
	Name      string
 | 
			
		||||
	ProxyType string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OpenConnectionPayload struct {
 | 
			
		||||
	ProxyName string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CloseConnectionPayload struct {
 | 
			
		||||
	ProxyName string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AddTrafficInPayload struct {
 | 
			
		||||
	ProxyName    string
 | 
			
		||||
	TrafficBytes int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AddTrafficOutPayload struct {
 | 
			
		||||
	ProxyName    string
 | 
			
		||||
	TrafficBytes int64
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								models/metrics/metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								models/metrics/metrics.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
package metrics
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/fatedier/frp/models/metrics/aggregate"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var EnableMem = aggregate.EnableMem
 | 
			
		||||
var EnablePrometheus = aggregate.EnablePrometheus
 | 
			
		||||
							
								
								
									
										95
									
								
								models/metrics/prometheus/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								models/metrics/prometheus/server.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
package prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/fatedier/frp/server/metrics"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	namespace       = "frp"
 | 
			
		||||
	serverSubsystem = "server"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ServerMetrics metrics.ServerMetrics = newServerMetrics()
 | 
			
		||||
 | 
			
		||||
type serverMetrics struct {
 | 
			
		||||
	clientCount     prometheus.Gauge
 | 
			
		||||
	proxyCount      *prometheus.GaugeVec
 | 
			
		||||
	connectionCount *prometheus.GaugeVec
 | 
			
		||||
	trafficIn       *prometheus.CounterVec
 | 
			
		||||
	trafficOut      *prometheus.CounterVec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) NewClient() {
 | 
			
		||||
	m.clientCount.Inc()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseClient() {
 | 
			
		||||
	m.clientCount.Dec()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) NewProxy(name string, proxyType string) {
 | 
			
		||||
	m.proxyCount.WithLabelValues(proxyType).Inc()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseProxy(name string, proxyType string) {
 | 
			
		||||
	m.proxyCount.WithLabelValues(proxyType).Dec()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
 | 
			
		||||
	m.connectionCount.WithLabelValues(name, proxyType).Inc()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) CloseConnection(name string, proxyType string) {
 | 
			
		||||
	m.connectionCount.WithLabelValues(name, proxyType).Dec()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
 | 
			
		||||
	m.trafficIn.WithLabelValues(name, proxyType).Add(float64(trafficBytes))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
 | 
			
		||||
	m.trafficOut.WithLabelValues(name, proxyType).Add(float64(trafficBytes))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newServerMetrics() *serverMetrics {
 | 
			
		||||
	m := &serverMetrics{
 | 
			
		||||
		clientCount: prometheus.NewGauge(prometheus.GaugeOpts{
 | 
			
		||||
			Namespace: namespace,
 | 
			
		||||
			Subsystem: serverSubsystem,
 | 
			
		||||
			Name:      "client_counts",
 | 
			
		||||
			Help:      "The current client counts of frps",
 | 
			
		||||
		}),
 | 
			
		||||
		proxyCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
 | 
			
		||||
			Namespace: namespace,
 | 
			
		||||
			Subsystem: serverSubsystem,
 | 
			
		||||
			Name:      "proxy_counts",
 | 
			
		||||
			Help:      "The current proxy counts",
 | 
			
		||||
		}, []string{"type"}),
 | 
			
		||||
		connectionCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
 | 
			
		||||
			Namespace: namespace,
 | 
			
		||||
			Subsystem: serverSubsystem,
 | 
			
		||||
			Name:      "connection_counts",
 | 
			
		||||
			Help:      "The current connection counts",
 | 
			
		||||
		}, []string{"name", "type"}),
 | 
			
		||||
		trafficIn: prometheus.NewCounterVec(prometheus.CounterOpts{
 | 
			
		||||
			Namespace: namespace,
 | 
			
		||||
			Subsystem: serverSubsystem,
 | 
			
		||||
			Name:      "traffic_in",
 | 
			
		||||
			Help:      "The total in traffic",
 | 
			
		||||
		}, []string{"name", "type"}),
 | 
			
		||||
		trafficOut: prometheus.NewCounterVec(prometheus.CounterOpts{
 | 
			
		||||
			Namespace: namespace,
 | 
			
		||||
			Subsystem: serverSubsystem,
 | 
			
		||||
			Name:      "traffic_out",
 | 
			
		||||
			Help:      "The total out traffic",
 | 
			
		||||
		}, []string{"name", "type"}),
 | 
			
		||||
	}
 | 
			
		||||
	prometheus.MustRegister(m.clientCount)
 | 
			
		||||
	prometheus.MustRegister(m.proxyCount)
 | 
			
		||||
	prometheus.MustRegister(m.connectionCount)
 | 
			
		||||
	prometheus.MustRegister(m.trafficIn)
 | 
			
		||||
	prometheus.MustRegister(m.trafficOut)
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
@@ -107,6 +107,9 @@ type NewProxy struct {
 | 
			
		||||
 | 
			
		||||
	// stcp
 | 
			
		||||
	Sk string `json:"sk"`
 | 
			
		||||
 | 
			
		||||
	// tcpmux
 | 
			
		||||
	Multiplexer string `json:"multiplexer"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NewProxyResp struct {
 | 
			
		||||
@@ -120,7 +123,9 @@ type CloseProxy struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NewWorkConn struct {
 | 
			
		||||
	RunId string `json:"run_id"`
 | 
			
		||||
	RunId        string `json:"run_id"`
 | 
			
		||||
	PrivilegeKey string `json:"privilege_key"`
 | 
			
		||||
	Timestamp    int64  `json:"timestamp"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReqWorkConn struct {
 | 
			
		||||
@@ -132,6 +137,7 @@ type StartWorkConn struct {
 | 
			
		||||
	DstAddr   string `json:"dst_addr"`
 | 
			
		||||
	SrcPort   uint16 `json:"src_port"`
 | 
			
		||||
	DstPort   uint16 `json:"dst_port"`
 | 
			
		||||
	Error     string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NewVisitorConn struct {
 | 
			
		||||
@@ -148,9 +154,12 @@ type NewVisitorConnResp struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Ping struct {
 | 
			
		||||
	PrivilegeKey string `json:"privilege_key"`
 | 
			
		||||
	Timestamp    int64  `json:"timestamp"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Pong struct {
 | 
			
		||||
	Error string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UdpPacket struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -84,6 +84,7 @@ func (p *httpPlugin) do(ctx context.Context, r *Request, res *Response) error {
 | 
			
		||||
	}
 | 
			
		||||
	req = req.WithContext(ctx)
 | 
			
		||||
	req.Header.Set("X-Frp-Reqid", GetReqidFromContext(ctx))
 | 
			
		||||
	req.Header.Set("Content-Type", "application/json")
 | 
			
		||||
	resp, err := p.client.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
 
 | 
			
		||||
@@ -23,14 +23,16 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/consts"
 | 
			
		||||
	frpErr "github.com/fatedier/frp/models/errors"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
	plugin "github.com/fatedier/frp/models/plugin/server"
 | 
			
		||||
	"github.com/fatedier/frp/server/controller"
 | 
			
		||||
	"github.com/fatedier/frp/server/metrics"
 | 
			
		||||
	"github.com/fatedier/frp/server/proxy"
 | 
			
		||||
	"github.com/fatedier/frp/server/stats"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
	"github.com/fatedier/frp/utils/version"
 | 
			
		||||
	"github.com/fatedier/frp/utils/xlog"
 | 
			
		||||
 | 
			
		||||
@@ -90,8 +92,8 @@ type Control struct {
 | 
			
		||||
	// plugin manager
 | 
			
		||||
	pluginManager *plugin.Manager
 | 
			
		||||
 | 
			
		||||
	// stats collector to store stats info of clients and proxies
 | 
			
		||||
	statsCollector stats.Collector
 | 
			
		||||
	// verifies authentication based on selected method
 | 
			
		||||
	authVerifier auth.Verifier
 | 
			
		||||
 | 
			
		||||
	// login message
 | 
			
		||||
	loginMsg *msg.Login
 | 
			
		||||
@@ -147,7 +149,7 @@ func NewControl(
 | 
			
		||||
	rc *controller.ResourceController,
 | 
			
		||||
	pxyManager *proxy.ProxyManager,
 | 
			
		||||
	pluginManager *plugin.Manager,
 | 
			
		||||
	statsCollector stats.Collector,
 | 
			
		||||
	authVerifier auth.Verifier,
 | 
			
		||||
	ctlConn net.Conn,
 | 
			
		||||
	loginMsg *msg.Login,
 | 
			
		||||
	serverCfg config.ServerCommonConf,
 | 
			
		||||
@@ -161,7 +163,7 @@ func NewControl(
 | 
			
		||||
		rc:              rc,
 | 
			
		||||
		pxyManager:      pxyManager,
 | 
			
		||||
		pluginManager:   pluginManager,
 | 
			
		||||
		statsCollector:  statsCollector,
 | 
			
		||||
		authVerifier:    authVerifier,
 | 
			
		||||
		conn:            ctlConn,
 | 
			
		||||
		loginMsg:        loginMsg,
 | 
			
		||||
		sendCh:          make(chan msg.Message, 10),
 | 
			
		||||
@@ -203,7 +205,7 @@ func (ctl *Control) Start() {
 | 
			
		||||
	go ctl.stoper()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctl *Control) RegisterWorkConn(conn net.Conn) {
 | 
			
		||||
func (ctl *Control) RegisterWorkConn(conn net.Conn) error {
 | 
			
		||||
	xl := ctl.xl
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := recover(); err != nil {
 | 
			
		||||
@@ -215,9 +217,10 @@ func (ctl *Control) RegisterWorkConn(conn net.Conn) {
 | 
			
		||||
	select {
 | 
			
		||||
	case ctl.workConnCh <- conn:
 | 
			
		||||
		xl.Debug("new work connection registered")
 | 
			
		||||
		return nil
 | 
			
		||||
	default:
 | 
			
		||||
		xl.Debug("work connection pool is full, discarding")
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return fmt.Errorf("work connection pool is full, discarding")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -373,16 +376,12 @@ func (ctl *Control) stoper() {
 | 
			
		||||
	for _, pxy := range ctl.proxies {
 | 
			
		||||
		pxy.Close()
 | 
			
		||||
		ctl.pxyManager.Del(pxy.GetName())
 | 
			
		||||
		ctl.statsCollector.Mark(stats.TypeCloseProxy, &stats.CloseProxyPayload{
 | 
			
		||||
			Name:      pxy.GetName(),
 | 
			
		||||
			ProxyType: pxy.GetConf().GetBaseInfo().ProxyType,
 | 
			
		||||
		})
 | 
			
		||||
		metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctl.allShutdown.Done()
 | 
			
		||||
	xl.Info("client exit success")
 | 
			
		||||
 | 
			
		||||
	ctl.statsCollector.Mark(stats.TypeCloseClient, &stats.CloseClientPayload{})
 | 
			
		||||
	metrics.Server.CloseClient()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// block until Control closed
 | 
			
		||||
@@ -438,21 +437,25 @@ func (ctl *Control) manager() {
 | 
			
		||||
					ProxyName: m.ProxyName,
 | 
			
		||||
				}
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					resp.Error = err.Error()
 | 
			
		||||
					xl.Warn("new proxy [%s] error: %v", m.ProxyName, err)
 | 
			
		||||
					resp.Error = util.GenerateResponseErrorString(fmt.Sprintf("new proxy [%s] error", m.ProxyName), err, ctl.serverCfg.DetailedErrorsToClient)
 | 
			
		||||
				} else {
 | 
			
		||||
					resp.RemoteAddr = remoteAddr
 | 
			
		||||
					xl.Info("new proxy [%s] success", m.ProxyName)
 | 
			
		||||
					ctl.statsCollector.Mark(stats.TypeNewProxy, &stats.NewProxyPayload{
 | 
			
		||||
						Name:      m.ProxyName,
 | 
			
		||||
						ProxyType: m.ProxyType,
 | 
			
		||||
					})
 | 
			
		||||
					metrics.Server.NewProxy(m.ProxyName, m.ProxyType)
 | 
			
		||||
				}
 | 
			
		||||
				ctl.sendCh <- resp
 | 
			
		||||
			case *msg.CloseProxy:
 | 
			
		||||
				ctl.CloseProxy(m)
 | 
			
		||||
				xl.Info("close proxy [%s] success", m.ProxyName)
 | 
			
		||||
			case *msg.Ping:
 | 
			
		||||
				if err := ctl.authVerifier.VerifyPing(m); err != nil {
 | 
			
		||||
					xl.Warn("received invalid ping: %v", err)
 | 
			
		||||
					ctl.sendCh <- &msg.Pong{
 | 
			
		||||
						Error: "invalid authentication in ping",
 | 
			
		||||
					}
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				ctl.lastPing = time.Now()
 | 
			
		||||
				xl.Debug("receive heartbeat")
 | 
			
		||||
				ctl.sendCh <- &msg.Pong{}
 | 
			
		||||
@@ -471,7 +474,7 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
 | 
			
		||||
 | 
			
		||||
	// NewProxy will return a interface Proxy.
 | 
			
		||||
	// In fact it create different proxies by different proxy type, we just call run() here.
 | 
			
		||||
	pxy, err := proxy.NewProxy(ctl.ctx, ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.serverCfg)
 | 
			
		||||
	pxy, err := proxy.NewProxy(ctl.ctx, ctl.runId, ctl.rc, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.serverCfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return remoteAddr, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -533,9 +536,6 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
 | 
			
		||||
	delete(ctl.proxies, closeMsg.ProxyName)
 | 
			
		||||
	ctl.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	ctl.statsCollector.Mark(stats.TypeCloseProxy, &stats.CloseProxyPayload{
 | 
			
		||||
		Name:      pxy.GetName(),
 | 
			
		||||
		ProxyType: pxy.GetConf().GetBaseInfo().ProxyType,
 | 
			
		||||
	})
 | 
			
		||||
	metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ import (
 | 
			
		||||
	"github.com/fatedier/frp/models/nathole"
 | 
			
		||||
	"github.com/fatedier/frp/server/group"
 | 
			
		||||
	"github.com/fatedier/frp/server/ports"
 | 
			
		||||
	"github.com/fatedier/frp/utils/tcpmux"
 | 
			
		||||
	"github.com/fatedier/frp/utils/vhost"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -46,4 +47,7 @@ type ResourceController struct {
 | 
			
		||||
 | 
			
		||||
	// Controller for nat hole connections
 | 
			
		||||
	NatHoleController *nathole.NatHoleController
 | 
			
		||||
 | 
			
		||||
	// TcpMux HTTP CONNECT multiplexer
 | 
			
		||||
	TcpMuxHttpConnectMuxer *tcpmux.HttpConnectTcpMuxer
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import (
 | 
			
		||||
	frpNet "github.com/fatedier/frp/utils/net"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus/promhttp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
@@ -38,6 +39,11 @@ func (svr *Service) RunDashboardServer(addr string, port int) (err error) {
 | 
			
		||||
	user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd
 | 
			
		||||
	router.Use(frpNet.NewHttpAuthMiddleware(user, passwd).Middleware)
 | 
			
		||||
 | 
			
		||||
	// metrics
 | 
			
		||||
	if svr.cfg.EnablePrometheus {
 | 
			
		||||
		router.Handle("/metrics", promhttp.Handler())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// api, see dashboard_api.go
 | 
			
		||||
	router.HandleFunc("/api/serverinfo", svr.ApiServerInfo).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/api/proxy/{type}", svr.ApiProxyByType).Methods("GET")
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/consts"
 | 
			
		||||
	"github.com/fatedier/frp/models/metrics/mem"
 | 
			
		||||
	"github.com/fatedier/frp/utils/log"
 | 
			
		||||
	"github.com/fatedier/frp/utils/version"
 | 
			
		||||
 | 
			
		||||
@@ -62,7 +63,7 @@ func (svr *Service) ApiServerInfo(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	log.Info("Http request: [%s]", r.URL.Path)
 | 
			
		||||
	serverStats := svr.statsCollector.GetServer()
 | 
			
		||||
	serverStats := mem.StatsCollector.GetServer()
 | 
			
		||||
	svrResp := ServerInfoResp{
 | 
			
		||||
		Version:           version.Full(),
 | 
			
		||||
		BindPort:          svr.cfg.BindPort,
 | 
			
		||||
@@ -95,6 +96,12 @@ type TcpOutConf struct {
 | 
			
		||||
	RemotePort int `json:"remote_port"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TcpMuxOutConf struct {
 | 
			
		||||
	BaseOutConf
 | 
			
		||||
	config.DomainConf
 | 
			
		||||
	Multiplexer string `json:"multiplexer"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UdpOutConf struct {
 | 
			
		||||
	BaseOutConf
 | 
			
		||||
	RemotePort int `json:"remote_port"`
 | 
			
		||||
@@ -124,6 +131,8 @@ func getConfByType(proxyType string) interface{} {
 | 
			
		||||
	switch proxyType {
 | 
			
		||||
	case consts.TcpProxy:
 | 
			
		||||
		return &TcpOutConf{}
 | 
			
		||||
	case consts.TcpMuxProxy:
 | 
			
		||||
		return &TcpMuxOutConf{}
 | 
			
		||||
	case consts.UdpProxy:
 | 
			
		||||
		return &UdpOutConf{}
 | 
			
		||||
	case consts.HttpProxy:
 | 
			
		||||
@@ -178,7 +187,7 @@ func (svr *Service) ApiProxyByType(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
 | 
			
		||||
	proxyStats := svr.statsCollector.GetProxiesByType(proxyType)
 | 
			
		||||
	proxyStats := mem.StatsCollector.GetProxiesByType(proxyType)
 | 
			
		||||
	proxyInfos = make([]*ProxyStatsInfo, 0, len(proxyStats))
 | 
			
		||||
	for _, ps := range proxyStats {
 | 
			
		||||
		proxyInfo := &ProxyStatsInfo{}
 | 
			
		||||
@@ -248,7 +257,7 @@ func (svr *Service) ApiProxyByTypeAndName(w http.ResponseWriter, r *http.Request
 | 
			
		||||
 | 
			
		||||
func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp, code int, msg string) {
 | 
			
		||||
	proxyInfo.Name = proxyName
 | 
			
		||||
	ps := svr.statsCollector.GetProxiesByTypeAndName(proxyType, proxyName)
 | 
			
		||||
	ps := mem.StatsCollector.GetProxiesByTypeAndName(proxyType, proxyName)
 | 
			
		||||
	if ps == nil {
 | 
			
		||||
		code = 404
 | 
			
		||||
		msg = "no proxy info found"
 | 
			
		||||
@@ -306,7 +315,7 @@ func (svr *Service) ApiProxyTraffic(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
 | 
			
		||||
	trafficResp := GetProxyTrafficResp{}
 | 
			
		||||
	trafficResp.Name = name
 | 
			
		||||
	proxyTrafficInfo := svr.statsCollector.GetProxyTraffic(name)
 | 
			
		||||
	proxyTrafficInfo := mem.StatsCollector.GetProxyTraffic(name)
 | 
			
		||||
 | 
			
		||||
	if proxyTrafficInfo == nil {
 | 
			
		||||
		res.Code = 404
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								server/metrics/metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								server/metrics/metrics.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
package metrics
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ServerMetrics interface {
 | 
			
		||||
	NewClient()
 | 
			
		||||
	CloseClient()
 | 
			
		||||
	NewProxy(name string, proxyType string)
 | 
			
		||||
	CloseProxy(name string, proxyType string)
 | 
			
		||||
	OpenConnection(name string, proxyType string)
 | 
			
		||||
	CloseConnection(name string, proxyType string)
 | 
			
		||||
	AddTrafficIn(name string, proxyType string, trafficBytes int64)
 | 
			
		||||
	AddTrafficOut(name string, proxyType string, trafficBytes int64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var Server ServerMetrics = noopServerMetrics{}
 | 
			
		||||
 | 
			
		||||
var registerMetrics sync.Once
 | 
			
		||||
 | 
			
		||||
func Register(m ServerMetrics) {
 | 
			
		||||
	registerMetrics.Do(func() {
 | 
			
		||||
		Server = m
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type noopServerMetrics struct{}
 | 
			
		||||
 | 
			
		||||
func (noopServerMetrics) NewClient()                                                      {}
 | 
			
		||||
func (noopServerMetrics) CloseClient()                                                    {}
 | 
			
		||||
func (noopServerMetrics) NewProxy(name string, proxyType string)                          {}
 | 
			
		||||
func (noopServerMetrics) CloseProxy(name string, proxyType string)                        {}
 | 
			
		||||
func (noopServerMetrics) OpenConnection(name string, proxyType string)                    {}
 | 
			
		||||
func (noopServerMetrics) CloseConnection(name string, proxyType string)                   {}
 | 
			
		||||
func (noopServerMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64)  {}
 | 
			
		||||
func (noopServerMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {}
 | 
			
		||||
@@ -20,7 +20,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/server/stats"
 | 
			
		||||
	"github.com/fatedier/frp/server/metrics"
 | 
			
		||||
	frpNet "github.com/fatedier/frp/utils/net"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
	"github.com/fatedier/frp/utils/vhost"
 | 
			
		||||
@@ -159,21 +159,16 @@ func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err err
 | 
			
		||||
	}
 | 
			
		||||
	workConn = frpNet.WrapReadWriteCloserToConn(rwc, tmpConn)
 | 
			
		||||
	workConn = frpNet.WrapStatsConn(workConn, pxy.updateStatsAfterClosedConn)
 | 
			
		||||
	pxy.statsCollector.Mark(stats.TypeOpenConnection, &stats.OpenConnectionPayload{ProxyName: pxy.GetName()})
 | 
			
		||||
	metrics.Server.OpenConnection(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *HttpProxy) updateStatsAfterClosedConn(totalRead, totalWrite int64) {
 | 
			
		||||
	name := pxy.GetName()
 | 
			
		||||
	pxy.statsCollector.Mark(stats.TypeCloseProxy, &stats.CloseConnectionPayload{ProxyName: name})
 | 
			
		||||
	pxy.statsCollector.Mark(stats.TypeAddTrafficIn, &stats.AddTrafficInPayload{
 | 
			
		||||
		ProxyName:    name,
 | 
			
		||||
		TrafficBytes: totalWrite,
 | 
			
		||||
	})
 | 
			
		||||
	pxy.statsCollector.Mark(stats.TypeAddTrafficOut, &stats.AddTrafficOutPayload{
 | 
			
		||||
		ProxyName:    name,
 | 
			
		||||
		TrafficBytes: totalRead,
 | 
			
		||||
	})
 | 
			
		||||
	proxyType := pxy.GetConf().GetBaseInfo().ProxyType
 | 
			
		||||
	metrics.Server.CloseConnection(name, proxyType)
 | 
			
		||||
	metrics.Server.AddTrafficIn(name, proxyType, totalWrite)
 | 
			
		||||
	metrics.Server.AddTrafficOut(name, proxyType, totalRead)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *HttpProxy) Close() {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ import (
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
	"github.com/fatedier/frp/server/controller"
 | 
			
		||||
	"github.com/fatedier/frp/server/stats"
 | 
			
		||||
	"github.com/fatedier/frp/server/metrics"
 | 
			
		||||
	frpNet "github.com/fatedier/frp/utils/net"
 | 
			
		||||
	"github.com/fatedier/frp/utils/xlog"
 | 
			
		||||
 | 
			
		||||
@@ -45,14 +45,13 @@ type Proxy interface {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BaseProxy struct {
 | 
			
		||||
	name           string
 | 
			
		||||
	rc             *controller.ResourceController
 | 
			
		||||
	statsCollector stats.Collector
 | 
			
		||||
	listeners      []net.Listener
 | 
			
		||||
	usedPortsNum   int
 | 
			
		||||
	poolCount      int
 | 
			
		||||
	getWorkConnFn  GetWorkConnFn
 | 
			
		||||
	serverCfg      config.ServerCommonConf
 | 
			
		||||
	name          string
 | 
			
		||||
	rc            *controller.ResourceController
 | 
			
		||||
	listeners     []net.Listener
 | 
			
		||||
	usedPortsNum  int
 | 
			
		||||
	poolCount     int
 | 
			
		||||
	getWorkConnFn GetWorkConnFn
 | 
			
		||||
	serverCfg     config.ServerCommonConf
 | 
			
		||||
 | 
			
		||||
	mu  sync.RWMutex
 | 
			
		||||
	xl  *xlog.Logger
 | 
			
		||||
@@ -116,6 +115,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
 | 
			
		||||
			SrcPort:   uint16(srcPort),
 | 
			
		||||
			DstAddr:   dstAddr,
 | 
			
		||||
			DstPort:   uint16(dstPort),
 | 
			
		||||
			Error:     "",
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			xl.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
 | 
			
		||||
@@ -135,7 +135,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
 | 
			
		||||
// startListenHandler start a goroutine handler for each listener.
 | 
			
		||||
// p: p will just be passed to handler(Proxy, frpNet.Conn).
 | 
			
		||||
// handler: each proxy type can set different handler function to deal with connections accepted from listeners.
 | 
			
		||||
func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn, stats.Collector, config.ServerCommonConf)) {
 | 
			
		||||
func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn, config.ServerCommonConf)) {
 | 
			
		||||
	xl := xlog.FromContextSafe(pxy.ctx)
 | 
			
		||||
	for _, listener := range pxy.listeners {
 | 
			
		||||
		go func(l net.Listener) {
 | 
			
		||||
@@ -148,26 +148,25 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn,
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				xl.Debug("get a user connection [%s]", c.RemoteAddr().String())
 | 
			
		||||
				go handler(p, c, pxy.statsCollector, pxy.serverCfg)
 | 
			
		||||
				go handler(p, c, pxy.serverCfg)
 | 
			
		||||
			}
 | 
			
		||||
		}(listener)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewProxy(ctx context.Context, runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int,
 | 
			
		||||
func NewProxy(ctx context.Context, runId string, rc *controller.ResourceController, poolCount int,
 | 
			
		||||
	getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf) (pxy Proxy, err error) {
 | 
			
		||||
 | 
			
		||||
	xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(pxyConf.GetBaseInfo().ProxyName)
 | 
			
		||||
	basePxy := BaseProxy{
 | 
			
		||||
		name:           pxyConf.GetBaseInfo().ProxyName,
 | 
			
		||||
		rc:             rc,
 | 
			
		||||
		statsCollector: statsCollector,
 | 
			
		||||
		listeners:      make([]net.Listener, 0),
 | 
			
		||||
		poolCount:      poolCount,
 | 
			
		||||
		getWorkConnFn:  getWorkConnFn,
 | 
			
		||||
		serverCfg:      serverCfg,
 | 
			
		||||
		xl:             xl,
 | 
			
		||||
		ctx:            xlog.NewContext(ctx, xl),
 | 
			
		||||
		name:          pxyConf.GetBaseInfo().ProxyName,
 | 
			
		||||
		rc:            rc,
 | 
			
		||||
		listeners:     make([]net.Listener, 0),
 | 
			
		||||
		poolCount:     poolCount,
 | 
			
		||||
		getWorkConnFn: getWorkConnFn,
 | 
			
		||||
		serverCfg:     serverCfg,
 | 
			
		||||
		xl:            xl,
 | 
			
		||||
		ctx:           xlog.NewContext(ctx, xl),
 | 
			
		||||
	}
 | 
			
		||||
	switch cfg := pxyConf.(type) {
 | 
			
		||||
	case *config.TcpProxyConf:
 | 
			
		||||
@@ -176,6 +175,11 @@ func NewProxy(ctx context.Context, runId string, rc *controller.ResourceControll
 | 
			
		||||
			BaseProxy: &basePxy,
 | 
			
		||||
			cfg:       cfg,
 | 
			
		||||
		}
 | 
			
		||||
	case *config.TcpMuxProxyConf:
 | 
			
		||||
		pxy = &TcpMuxProxy{
 | 
			
		||||
			BaseProxy: &basePxy,
 | 
			
		||||
			cfg:       cfg,
 | 
			
		||||
		}
 | 
			
		||||
	case *config.HttpProxyConf:
 | 
			
		||||
		pxy = &HttpProxy{
 | 
			
		||||
			BaseProxy: &basePxy,
 | 
			
		||||
@@ -210,7 +214,7 @@ func NewProxy(ctx context.Context, runId string, rc *controller.ResourceControll
 | 
			
		||||
 | 
			
		||||
// HandleUserTcpConnection is used for incoming tcp user connections.
 | 
			
		||||
// It can be used for tcp, http, https type.
 | 
			
		||||
func HandleUserTcpConnection(pxy Proxy, userConn net.Conn, statsCollector stats.Collector, serverCfg config.ServerCommonConf) {
 | 
			
		||||
func HandleUserTcpConnection(pxy Proxy, userConn net.Conn, serverCfg config.ServerCommonConf) {
 | 
			
		||||
	xl := xlog.FromContextSafe(pxy.Context())
 | 
			
		||||
	defer userConn.Close()
 | 
			
		||||
 | 
			
		||||
@@ -237,17 +241,13 @@ func HandleUserTcpConnection(pxy Proxy, userConn net.Conn, statsCollector stats.
 | 
			
		||||
	xl.Debug("join connections, workConn(l[%s] r[%s]) userConn(l[%s] r[%s])", workConn.LocalAddr().String(),
 | 
			
		||||
		workConn.RemoteAddr().String(), userConn.LocalAddr().String(), userConn.RemoteAddr().String())
 | 
			
		||||
 | 
			
		||||
	statsCollector.Mark(stats.TypeOpenConnection, &stats.OpenConnectionPayload{ProxyName: pxy.GetName()})
 | 
			
		||||
	name := pxy.GetName()
 | 
			
		||||
	proxyType := pxy.GetConf().GetBaseInfo().ProxyType
 | 
			
		||||
	metrics.Server.OpenConnection(name, proxyType)
 | 
			
		||||
	inCount, outCount := frpIo.Join(local, userConn)
 | 
			
		||||
	statsCollector.Mark(stats.TypeCloseConnection, &stats.CloseConnectionPayload{ProxyName: pxy.GetName()})
 | 
			
		||||
	statsCollector.Mark(stats.TypeAddTrafficIn, &stats.AddTrafficInPayload{
 | 
			
		||||
		ProxyName:    pxy.GetName(),
 | 
			
		||||
		TrafficBytes: inCount,
 | 
			
		||||
	})
 | 
			
		||||
	statsCollector.Mark(stats.TypeAddTrafficOut, &stats.AddTrafficOutPayload{
 | 
			
		||||
		ProxyName:    pxy.GetName(),
 | 
			
		||||
		TrafficBytes: outCount,
 | 
			
		||||
	})
 | 
			
		||||
	metrics.Server.CloseConnection(name, proxyType)
 | 
			
		||||
	metrics.Server.AddTrafficIn(name, proxyType, inCount)
 | 
			
		||||
	metrics.Server.AddTrafficOut(name, proxyType, outCount)
 | 
			
		||||
	xl.Debug("join connections closed")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								server/proxy/tcpmux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								server/proxy/tcpmux.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
// Copyright 2020 guylewin, guy@lewin.co.il
 | 
			
		||||
//
 | 
			
		||||
// 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 proxy
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/consts"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
	"github.com/fatedier/frp/utils/vhost"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type TcpMuxProxy struct {
 | 
			
		||||
	*BaseProxy
 | 
			
		||||
	cfg *config.TcpMuxProxyConf
 | 
			
		||||
 | 
			
		||||
	realPort int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) httpConnectListen(domain string, addrs []string) ([]string, error) {
 | 
			
		||||
	routeConfig := &vhost.VhostRouteConfig{
 | 
			
		||||
		Domain: domain,
 | 
			
		||||
	}
 | 
			
		||||
	l, err := pxy.rc.TcpMuxHttpConnectMuxer.Listen(pxy.ctx, routeConfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pxy.xl.Info("tcpmux httpconnect multiplexer listens for host [%s]", routeConfig.Domain)
 | 
			
		||||
	pxy.listeners = append(pxy.listeners, l)
 | 
			
		||||
	return append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.TcpMuxHttpConnectPort)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) httpConnectRun() (remoteAddr string, err error) {
 | 
			
		||||
	addrs := make([]string, 0)
 | 
			
		||||
	for _, domain := range pxy.cfg.CustomDomains {
 | 
			
		||||
		if domain == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		addrs, err = pxy.httpConnectListen(domain, addrs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pxy.cfg.SubDomain != "" {
 | 
			
		||||
		addrs, err = pxy.httpConnectListen(pxy.cfg.SubDomain+"."+pxy.serverCfg.SubDomainHost, addrs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pxy.startListenHandler(pxy, HandleUserTcpConnection)
 | 
			
		||||
	remoteAddr = strings.Join(addrs, ",")
 | 
			
		||||
	return remoteAddr, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) Run() (remoteAddr string, err error) {
 | 
			
		||||
	switch pxy.cfg.Multiplexer {
 | 
			
		||||
	case consts.HttpConnectTcpMultiplexer:
 | 
			
		||||
		remoteAddr, err = pxy.httpConnectRun()
 | 
			
		||||
	default:
 | 
			
		||||
		err = fmt.Errorf("unknown multiplexer [%s]", pxy.cfg.Multiplexer)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		pxy.Close()
 | 
			
		||||
	}
 | 
			
		||||
	return remoteAddr, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) GetConf() config.ProxyConf {
 | 
			
		||||
	return pxy.cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *TcpMuxProxy) Close() {
 | 
			
		||||
	pxy.BaseProxy.Close()
 | 
			
		||||
	if pxy.cfg.Group == "" {
 | 
			
		||||
		pxy.rc.TcpPortManager.Release(pxy.realPort)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,7 @@ import (
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
	"github.com/fatedier/frp/models/proto/udp"
 | 
			
		||||
	"github.com/fatedier/frp/server/stats"
 | 
			
		||||
	"github.com/fatedier/frp/server/metrics"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/golib/errors"
 | 
			
		||||
)
 | 
			
		||||
@@ -114,10 +114,11 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 | 
			
		||||
				if errRet := errors.PanicToError(func() {
 | 
			
		||||
					xl.Trace("get udp message from workConn: %s", m.Content)
 | 
			
		||||
					pxy.readCh <- m
 | 
			
		||||
					pxy.statsCollector.Mark(stats.TypeAddTrafficOut, &stats.AddTrafficOutPayload{
 | 
			
		||||
						ProxyName:    pxy.GetName(),
 | 
			
		||||
						TrafficBytes: int64(len(m.Content)),
 | 
			
		||||
					})
 | 
			
		||||
					metrics.Server.AddTrafficOut(
 | 
			
		||||
						pxy.GetName(),
 | 
			
		||||
						pxy.GetConf().GetBaseInfo().ProxyType,
 | 
			
		||||
						int64(len(m.Content)),
 | 
			
		||||
					)
 | 
			
		||||
				}); errRet != nil {
 | 
			
		||||
					conn.Close()
 | 
			
		||||
					xl.Info("reader goroutine for udp work connection closed")
 | 
			
		||||
@@ -143,10 +144,11 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 | 
			
		||||
					return
 | 
			
		||||
				} else {
 | 
			
		||||
					xl.Trace("send message to udp workConn: %s", udpMsg.Content)
 | 
			
		||||
					pxy.statsCollector.Mark(stats.TypeAddTrafficIn, &stats.AddTrafficInPayload{
 | 
			
		||||
						ProxyName:    pxy.GetName(),
 | 
			
		||||
						TrafficBytes: int64(len(udpMsg.Content)),
 | 
			
		||||
					})
 | 
			
		||||
					metrics.Server.AddTrafficIn(
 | 
			
		||||
						pxy.GetName(),
 | 
			
		||||
						pxy.GetConf().GetBaseInfo().ProxyType,
 | 
			
		||||
						int64(len(udpMsg.Content)),
 | 
			
		||||
					)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			case <-ctx.Done():
 | 
			
		||||
 
 | 
			
		||||
@@ -30,17 +30,20 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/assets"
 | 
			
		||||
	"github.com/fatedier/frp/models/auth"
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	modelmetrics "github.com/fatedier/frp/models/metrics"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
	"github.com/fatedier/frp/models/nathole"
 | 
			
		||||
	plugin "github.com/fatedier/frp/models/plugin/server"
 | 
			
		||||
	"github.com/fatedier/frp/server/controller"
 | 
			
		||||
	"github.com/fatedier/frp/server/group"
 | 
			
		||||
	"github.com/fatedier/frp/server/metrics"
 | 
			
		||||
	"github.com/fatedier/frp/server/ports"
 | 
			
		||||
	"github.com/fatedier/frp/server/proxy"
 | 
			
		||||
	"github.com/fatedier/frp/server/stats"
 | 
			
		||||
	"github.com/fatedier/frp/utils/log"
 | 
			
		||||
	frpNet "github.com/fatedier/frp/utils/net"
 | 
			
		||||
	"github.com/fatedier/frp/utils/tcpmux"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
	"github.com/fatedier/frp/utils/version"
 | 
			
		||||
	"github.com/fatedier/frp/utils/vhost"
 | 
			
		||||
@@ -51,7 +54,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	connReadTimeout time.Duration = 10 * time.Second
 | 
			
		||||
	connReadTimeout       time.Duration = 10 * time.Second
 | 
			
		||||
	vhostReadWriteTimeout time.Duration = 30 * time.Second
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Server service
 | 
			
		||||
@@ -86,8 +90,8 @@ type Service struct {
 | 
			
		||||
	// All resource managers and controllers
 | 
			
		||||
	rc *controller.ResourceController
 | 
			
		||||
 | 
			
		||||
	// stats collector to store server and proxies stats info
 | 
			
		||||
	statsCollector stats.Collector
 | 
			
		||||
	// Verifies authentication based on selected method
 | 
			
		||||
	authVerifier auth.Verifier
 | 
			
		||||
 | 
			
		||||
	tlsConfig *tls.Config
 | 
			
		||||
 | 
			
		||||
@@ -105,6 +109,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
 | 
			
		||||
			UdpPortManager: ports.NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts),
 | 
			
		||||
		},
 | 
			
		||||
		httpVhostRouter: vhost.NewVhostRouters(),
 | 
			
		||||
		authVerifier:    auth.NewAuthVerifier(cfg.AuthServerConfig),
 | 
			
		||||
		tlsConfig:       generateTLSConfig(),
 | 
			
		||||
		cfg:             cfg,
 | 
			
		||||
	}
 | 
			
		||||
@@ -207,7 +212,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, 30*time.Second)
 | 
			
		||||
		svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, vhostReadWriteTimeout)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err = fmt.Errorf("Create vhost httpsMuxer error, %v", err)
 | 
			
		||||
			return
 | 
			
		||||
@@ -215,6 +220,23 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
 | 
			
		||||
		log.Info("https service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHttpsPort)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create tcpmux httpconnect multiplexer.
 | 
			
		||||
	if cfg.TcpMuxHttpConnectPort > 0 {
 | 
			
		||||
		var l net.Listener
 | 
			
		||||
		l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.TcpMuxHttpConnectPort))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err = fmt.Errorf("Create server listener error, %v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		svr.rc.TcpMuxHttpConnectMuxer, err = tcpmux.NewHttpConnectTcpMuxer(l, vhostReadWriteTimeout)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err = fmt.Errorf("Create vhost tcpMuxer error, %v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		log.Info("tcpmux httpconnect multiplexer listen on %s:%d", cfg.ProxyBindAddr, cfg.TcpMuxHttpConnectPort)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// frp tls listener
 | 
			
		||||
	svr.tlsListener = svr.muxer.Listen(1, 1, func(data []byte) bool {
 | 
			
		||||
		return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE
 | 
			
		||||
@@ -251,8 +273,12 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
 | 
			
		||||
		log.Info("Dashboard listen on %s:%d", cfg.DashboardAddr, cfg.DashboardPort)
 | 
			
		||||
		statsEnable = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	svr.statsCollector = stats.NewInternalCollector(statsEnable)
 | 
			
		||||
	if statsEnable {
 | 
			
		||||
		modelmetrics.EnableMem()
 | 
			
		||||
		if cfg.EnablePrometheus {
 | 
			
		||||
			modelmetrics.EnablePrometheus()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -284,7 +310,7 @@ func (svr *Service) HandleListener(l net.Listener) {
 | 
			
		||||
 | 
			
		||||
		log.Trace("start check TLS connection...")
 | 
			
		||||
		originConn := c
 | 
			
		||||
		c, err = frpNet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, connReadTimeout)
 | 
			
		||||
		c, err = frpNet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, svr.cfg.TlsOnly, connReadTimeout)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Warn("CheckAndEnableTLSServerConnWithTimeout error: %v", err)
 | 
			
		||||
			originConn.Close()
 | 
			
		||||
@@ -322,18 +348,20 @@ func (svr *Service) HandleListener(l net.Listener) {
 | 
			
		||||
						xl.Warn("register control error: %v", err)
 | 
			
		||||
						msg.WriteMsg(conn, &msg.LoginResp{
 | 
			
		||||
							Version: version.Full(),
 | 
			
		||||
							Error:   err.Error(),
 | 
			
		||||
							Error:   util.GenerateResponseErrorString("register control error", err, svr.cfg.DetailedErrorsToClient),
 | 
			
		||||
						})
 | 
			
		||||
						conn.Close()
 | 
			
		||||
					}
 | 
			
		||||
				case *msg.NewWorkConn:
 | 
			
		||||
					svr.RegisterWorkConn(conn, m)
 | 
			
		||||
					if err := svr.RegisterWorkConn(conn, m); err != nil {
 | 
			
		||||
						conn.Close()
 | 
			
		||||
					}
 | 
			
		||||
				case *msg.NewVisitorConn:
 | 
			
		||||
					if err = svr.RegisterVisitorConn(conn, m); err != nil {
 | 
			
		||||
						xl.Warn("register visitor conn error: %v", err)
 | 
			
		||||
						msg.WriteMsg(conn, &msg.NewVisitorConnResp{
 | 
			
		||||
							ProxyName: m.ProxyName,
 | 
			
		||||
							Error:     err.Error(),
 | 
			
		||||
							Error:     util.GenerateResponseErrorString("register visitor conn error", err, svr.cfg.DetailedErrorsToClient),
 | 
			
		||||
						})
 | 
			
		||||
						conn.Close()
 | 
			
		||||
					} else {
 | 
			
		||||
@@ -399,13 +427,11 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check auth.
 | 
			
		||||
	if util.GetAuthKey(svr.cfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
 | 
			
		||||
		err = fmt.Errorf("authorization failed")
 | 
			
		||||
	if err = svr.authVerifier.VerifyLogin(loginMsg); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctl := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg)
 | 
			
		||||
 | 
			
		||||
	ctl := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, svr.authVerifier, ctlConn, loginMsg, svr.cfg)
 | 
			
		||||
	if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil {
 | 
			
		||||
		oldCtl.allShutdown.WaitDone()
 | 
			
		||||
	}
 | 
			
		||||
@@ -413,7 +439,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err
 | 
			
		||||
	ctl.Start()
 | 
			
		||||
 | 
			
		||||
	// for statistics
 | 
			
		||||
	svr.statsCollector.Mark(stats.TypeNewClient, &stats.NewClientPayload{})
 | 
			
		||||
	metrics.Server.NewClient()
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		// block until control closed
 | 
			
		||||
@@ -424,15 +450,22 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterWorkConn register a new work connection to control and proxies need it.
 | 
			
		||||
func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) {
 | 
			
		||||
func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) error {
 | 
			
		||||
	xl := frpNet.NewLogFromConn(workConn)
 | 
			
		||||
	ctl, exist := svr.ctlManager.GetById(newMsg.RunId)
 | 
			
		||||
	if !exist {
 | 
			
		||||
		xl.Warn("No client control found for run id [%s]", newMsg.RunId)
 | 
			
		||||
		return
 | 
			
		||||
		return fmt.Errorf("no client control found for run id [%s]", newMsg.RunId)
 | 
			
		||||
	}
 | 
			
		||||
	ctl.RegisterWorkConn(workConn)
 | 
			
		||||
	return
 | 
			
		||||
	// Check auth.
 | 
			
		||||
	if err := svr.authVerifier.VerifyNewWorkConn(newMsg); err != nil {
 | 
			
		||||
		xl.Warn("Invalid authentication in NewWorkConn message on run id [%s]", newMsg.RunId)
 | 
			
		||||
		msg.WriteMsg(workConn, &msg.StartWorkConn{
 | 
			
		||||
			Error: "invalid authentication in NewWorkConn",
 | 
			
		||||
		})
 | 
			
		||||
		return fmt.Errorf("invalid authentication in NewWorkConn message on run id [%s]", newMsg.RunId)
 | 
			
		||||
	}
 | 
			
		||||
	return ctl.RegisterWorkConn(workConn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVisitorConn) error {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,277 +0,0 @@
 | 
			
		||||
// Copyright 2019 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 stats
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/utils/log"
 | 
			
		||||
	"github.com/fatedier/frp/utils/metric"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type internalCollector struct {
 | 
			
		||||
	enable bool
 | 
			
		||||
	info   *ServerStatistics
 | 
			
		||||
	mu     sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewInternalCollector(enable bool) Collector {
 | 
			
		||||
	return &internalCollector{
 | 
			
		||||
		enable: enable,
 | 
			
		||||
		info: &ServerStatistics{
 | 
			
		||||
			TotalTrafficIn:  metric.NewDateCounter(ReserveDays),
 | 
			
		||||
			TotalTrafficOut: metric.NewDateCounter(ReserveDays),
 | 
			
		||||
			CurConns:        metric.NewCounter(),
 | 
			
		||||
 | 
			
		||||
			ClientCounts:    metric.NewCounter(),
 | 
			
		||||
			ProxyTypeCounts: make(map[string]metric.Counter),
 | 
			
		||||
 | 
			
		||||
			ProxyStatistics: make(map[string]*ProxyStatistics),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) Run() error {
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			time.Sleep(12 * time.Hour)
 | 
			
		||||
			log.Debug("start to clear useless proxy statistics data...")
 | 
			
		||||
			collector.ClearUselessInfo()
 | 
			
		||||
			log.Debug("finish to clear useless proxy statistics data")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) ClearUselessInfo() {
 | 
			
		||||
	// To check if there are proxies that closed than 7 days and drop them.
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
	for name, data := range collector.info.ProxyStatistics {
 | 
			
		||||
		if !data.LastCloseTime.IsZero() && time.Since(data.LastCloseTime) > time.Duration(7*24)*time.Hour {
 | 
			
		||||
			delete(collector.info.ProxyStatistics, name)
 | 
			
		||||
			log.Trace("clear proxy [%s]'s statistics data, lastCloseTime: [%s]", name, data.LastCloseTime.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) Mark(statsType StatsType, payload interface{}) {
 | 
			
		||||
	if !collector.enable {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch v := payload.(type) {
 | 
			
		||||
	case *NewClientPayload:
 | 
			
		||||
		collector.newClient(v)
 | 
			
		||||
	case *CloseClientPayload:
 | 
			
		||||
		collector.closeClient(v)
 | 
			
		||||
	case *NewProxyPayload:
 | 
			
		||||
		collector.newProxy(v)
 | 
			
		||||
	case *CloseProxyPayload:
 | 
			
		||||
		collector.closeProxy(v)
 | 
			
		||||
	case *OpenConnectionPayload:
 | 
			
		||||
		collector.openConnection(v)
 | 
			
		||||
	case *CloseConnectionPayload:
 | 
			
		||||
		collector.closeConnection(v)
 | 
			
		||||
	case *AddTrafficInPayload:
 | 
			
		||||
		collector.addTrafficIn(v)
 | 
			
		||||
	case *AddTrafficOutPayload:
 | 
			
		||||
		collector.addTrafficOut(v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) newClient(payload *NewClientPayload) {
 | 
			
		||||
	collector.info.ClientCounts.Inc(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) closeClient(payload *CloseClientPayload) {
 | 
			
		||||
	collector.info.ClientCounts.Dec(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) newProxy(payload *NewProxyPayload) {
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
	counter, ok := collector.info.ProxyTypeCounts[payload.ProxyType]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		counter = metric.NewCounter()
 | 
			
		||||
	}
 | 
			
		||||
	counter.Inc(1)
 | 
			
		||||
	collector.info.ProxyTypeCounts[payload.ProxyType] = counter
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := collector.info.ProxyStatistics[payload.Name]
 | 
			
		||||
	if !(ok && proxyStats.ProxyType == payload.ProxyType) {
 | 
			
		||||
		proxyStats = &ProxyStatistics{
 | 
			
		||||
			Name:       payload.Name,
 | 
			
		||||
			ProxyType:  payload.ProxyType,
 | 
			
		||||
			CurConns:   metric.NewCounter(),
 | 
			
		||||
			TrafficIn:  metric.NewDateCounter(ReserveDays),
 | 
			
		||||
			TrafficOut: metric.NewDateCounter(ReserveDays),
 | 
			
		||||
		}
 | 
			
		||||
		collector.info.ProxyStatistics[payload.Name] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
	proxyStats.LastStartTime = time.Now()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) closeProxy(payload *CloseProxyPayload) {
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
	if counter, ok := collector.info.ProxyTypeCounts[payload.ProxyType]; ok {
 | 
			
		||||
		counter.Dec(1)
 | 
			
		||||
	}
 | 
			
		||||
	if proxyStats, ok := collector.info.ProxyStatistics[payload.Name]; ok {
 | 
			
		||||
		proxyStats.LastCloseTime = time.Now()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) openConnection(payload *OpenConnectionPayload) {
 | 
			
		||||
	collector.info.CurConns.Inc(1)
 | 
			
		||||
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
	proxyStats, ok := collector.info.ProxyStatistics[payload.ProxyName]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.CurConns.Inc(1)
 | 
			
		||||
		collector.info.ProxyStatistics[payload.ProxyName] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) closeConnection(payload *CloseConnectionPayload) {
 | 
			
		||||
	collector.info.CurConns.Dec(1)
 | 
			
		||||
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
	proxyStats, ok := collector.info.ProxyStatistics[payload.ProxyName]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.CurConns.Dec(1)
 | 
			
		||||
		collector.info.ProxyStatistics[payload.ProxyName] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) addTrafficIn(payload *AddTrafficInPayload) {
 | 
			
		||||
	collector.info.TotalTrafficIn.Inc(payload.TrafficBytes)
 | 
			
		||||
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := collector.info.ProxyStatistics[payload.ProxyName]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.TrafficIn.Inc(payload.TrafficBytes)
 | 
			
		||||
		collector.info.ProxyStatistics[payload.ProxyName] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) addTrafficOut(payload *AddTrafficOutPayload) {
 | 
			
		||||
	collector.info.TotalTrafficOut.Inc(payload.TrafficBytes)
 | 
			
		||||
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := collector.info.ProxyStatistics[payload.ProxyName]
 | 
			
		||||
	if ok {
 | 
			
		||||
		proxyStats.TrafficOut.Inc(payload.TrafficBytes)
 | 
			
		||||
		collector.info.ProxyStatistics[payload.ProxyName] = proxyStats
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) GetServer() *ServerStats {
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
	s := &ServerStats{
 | 
			
		||||
		TotalTrafficIn:  collector.info.TotalTrafficIn.TodayCount(),
 | 
			
		||||
		TotalTrafficOut: collector.info.TotalTrafficOut.TodayCount(),
 | 
			
		||||
		CurConns:        collector.info.CurConns.Count(),
 | 
			
		||||
		ClientCounts:    collector.info.ClientCounts.Count(),
 | 
			
		||||
		ProxyTypeCounts: make(map[string]int64),
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range collector.info.ProxyTypeCounts {
 | 
			
		||||
		s.ProxyTypeCounts[k] = v.Count()
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) GetProxiesByType(proxyType string) []*ProxyStats {
 | 
			
		||||
	res := make([]*ProxyStats, 0)
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	for name, proxyStats := range collector.info.ProxyStatistics {
 | 
			
		||||
		if proxyStats.ProxyType != proxyType {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ps := &ProxyStats{
 | 
			
		||||
			Name:            name,
 | 
			
		||||
			Type:            proxyStats.ProxyType,
 | 
			
		||||
			TodayTrafficIn:  proxyStats.TrafficIn.TodayCount(),
 | 
			
		||||
			TodayTrafficOut: proxyStats.TrafficOut.TodayCount(),
 | 
			
		||||
			CurConns:        proxyStats.CurConns.Count(),
 | 
			
		||||
		}
 | 
			
		||||
		if !proxyStats.LastStartTime.IsZero() {
 | 
			
		||||
			ps.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05")
 | 
			
		||||
		}
 | 
			
		||||
		if !proxyStats.LastCloseTime.IsZero() {
 | 
			
		||||
			ps.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05")
 | 
			
		||||
		}
 | 
			
		||||
		res = append(res, ps)
 | 
			
		||||
	}
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) GetProxiesByTypeAndName(proxyType string, proxyName string) (res *ProxyStats) {
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	for name, proxyStats := range collector.info.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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (collector *internalCollector) GetProxyTraffic(name string) (res *ProxyTrafficInfo) {
 | 
			
		||||
	collector.mu.Lock()
 | 
			
		||||
	defer collector.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	proxyStats, ok := collector.info.ProxyStatistics[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		res = &ProxyTrafficInfo{
 | 
			
		||||
			Name: name,
 | 
			
		||||
		}
 | 
			
		||||
		res.TrafficIn = proxyStats.TrafficIn.GetLastDaysCount(ReserveDays)
 | 
			
		||||
		res.TrafficOut = proxyStats.TrafficOut.GetLastDaysCount(ReserveDays)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								tests/ci/auth_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								tests/ci/auth_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
package ci
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/tests/config"
 | 
			
		||||
	"github.com/fatedier/frp/tests/consts"
 | 
			
		||||
	"github.com/fatedier/frp/tests/util"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const FRPS_TOKEN_TCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
bind_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
authentication_method = token
 | 
			
		||||
token = 123456
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const FRPC_TOKEN_TCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = 127.0.0.1
 | 
			
		||||
server_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
authentication_method = token
 | 
			
		||||
token = 123456
 | 
			
		||||
protocol = tcp
 | 
			
		||||
 | 
			
		||||
[tcp]
 | 
			
		||||
type = tcp
 | 
			
		||||
local_port = 10701
 | 
			
		||||
remote_port = 20801
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func TestTcpWithTokenAuthentication(t *testing.T) {
 | 
			
		||||
	assert := assert.New(t)
 | 
			
		||||
	frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TOKEN_TCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpsCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TOKEN_TCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpcCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
 | 
			
		||||
	err = frpsProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
 | 
			
		||||
	err = frpcProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
	assert.Equal(consts.TEST_TCP_ECHO_STR, res)
 | 
			
		||||
}
 | 
			
		||||
@@ -126,6 +126,13 @@ custom_domains = test6.frp.com
 | 
			
		||||
host_header_rewrite = test6.frp.com
 | 
			
		||||
header_X-From-Where = frp
 | 
			
		||||
 | 
			
		||||
[tcpmuxhttpconnect]
 | 
			
		||||
type = tcpmux
 | 
			
		||||
multiplexer = httpconnect
 | 
			
		||||
local_ip = 127.0.0.1
 | 
			
		||||
local_port = 10701
 | 
			
		||||
custom_domains = tunnel1
 | 
			
		||||
 | 
			
		||||
[wildcard_http]
 | 
			
		||||
type = http
 | 
			
		||||
local_ip = 127.0.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
bind_port = 10700
 | 
			
		||||
vhost_http_port = 10804
 | 
			
		||||
tcpmux_httpconnect_port = 10806
 | 
			
		||||
log_level = trace
 | 
			
		||||
token = 123456
 | 
			
		||||
allow_ports = 10000-20000,20002,30000-50000
 | 
			
		||||
 
 | 
			
		||||
@@ -212,6 +212,17 @@ func TestHttp(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestTcpMux(t *testing.T) {
 | 
			
		||||
	assert := assert.New(t)
 | 
			
		||||
 | 
			
		||||
	conn, err := gnet.DialTcpByProxy(fmt.Sprintf("http://%s:%d", "127.0.0.1", consts.TEST_TCP_MUX_FRP_PORT), "tunnel1")
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		res, err := util.SendTcpMsgByConn(conn, consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
		assert.NoError(err)
 | 
			
		||||
		assert.Equal(consts.TEST_TCP_ECHO_STR, res)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWebSocket(t *testing.T) {
 | 
			
		||||
	assert := assert.New(t)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -186,3 +186,95 @@ func TestTLSOverWebsocket(t *testing.T) {
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
	assert.Equal(consts.TEST_TCP_ECHO_STR, res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FRPS_TLS_ONLY_TCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
bind_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
tls_only = true
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const FRPC_TLS_ONLY_TCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = 127.0.0.1
 | 
			
		||||
server_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
protocol = tcp
 | 
			
		||||
tls_enable = true
 | 
			
		||||
 | 
			
		||||
[tcp]
 | 
			
		||||
type = tcp
 | 
			
		||||
local_port = 10701
 | 
			
		||||
remote_port = 20801
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const FRPC_TLS_ONLY_NO_TLS_TCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = 127.0.0.1
 | 
			
		||||
server_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
protocol = tcp
 | 
			
		||||
tls_enable = false
 | 
			
		||||
 | 
			
		||||
[tcp]
 | 
			
		||||
type = tcp
 | 
			
		||||
local_port = 10701
 | 
			
		||||
remote_port = 20802
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func TestTlsOnlyOverTCP(t *testing.T) {
 | 
			
		||||
	assert := assert.New(t)
 | 
			
		||||
	frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TLS_ONLY_TCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpsCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpcWithTlsCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TLS_ONLY_TCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpcWithTlsCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
 | 
			
		||||
	err = frpsProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcessWithTls := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcWithTlsCfgPath})
 | 
			
		||||
	err = frpcProcessWithTls.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpcProcessWithTls.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp over tls
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
	assert.Equal(consts.TEST_TCP_ECHO_STR, res)
 | 
			
		||||
	frpcProcessWithTls.Stop()
 | 
			
		||||
 | 
			
		||||
	frpcWithoutTlsCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TLS_ONLY_NO_TLS_TCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpcWithTlsCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpcProcessWithoutTls := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcWithoutTlsCfgPath})
 | 
			
		||||
	err = frpcProcessWithoutTls.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpcProcessWithoutTls.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp without tls
 | 
			
		||||
	_, err = util.SendTcpMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
	assert.Error(err)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,8 @@ var (
 | 
			
		||||
	TEST_HTTP_FOO_STR    string = "http foo string: " + TEST_STR
 | 
			
		||||
	TEST_HTTP_BAR_STR    string = "http bar string: " + TEST_STR
 | 
			
		||||
 | 
			
		||||
	TEST_TCP_MUX_FRP_PORT int = 10806
 | 
			
		||||
 | 
			
		||||
	TEST_STCP_FRP_PORT    int    = 10805
 | 
			
		||||
	TEST_STCP_EC_FRP_PORT int    = 10905
 | 
			
		||||
	TEST_STCP_ECHO_STR    string = "stcp type:" + TEST_STR
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
// Copyright 2018 fatedier, fatedier@gmail.com
 | 
			
		||||
// Copyright 2020 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.
 | 
			
		||||
@@ -12,19 +12,23 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package errors
 | 
			
		||||
package metric
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func PanicToError(fn func()) (err error) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			err = fmt.Errorf("Panic error: %v", r)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	fn()
 | 
			
		||||
	return
 | 
			
		||||
// GaugeMetric represents a single numerical value that can arbitrarily go up
 | 
			
		||||
// and down.
 | 
			
		||||
type GaugeMetric interface {
 | 
			
		||||
	Inc()
 | 
			
		||||
	Dec()
 | 
			
		||||
	Set(float64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CounterMetric represents a single numerical value that only ever
 | 
			
		||||
// goes up.
 | 
			
		||||
type CounterMetric interface {
 | 
			
		||||
	Inc()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HistogramMetric counts individual observations.
 | 
			
		||||
type HistogramMetric interface {
 | 
			
		||||
	Observe(float64)
 | 
			
		||||
}
 | 
			
		||||
@@ -16,6 +16,7 @@ package net
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +33,7 @@ func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out net.Conn) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, timeout time.Duration) (out net.Conn, err error) {
 | 
			
		||||
func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration) (out net.Conn, err error) {
 | 
			
		||||
	sc, r := gnet.NewSharedConnSize(c, 2)
 | 
			
		||||
	buf := make([]byte, 1)
 | 
			
		||||
	var n int
 | 
			
		||||
@@ -46,6 +47,10 @@ func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, t
 | 
			
		||||
	if n == 1 && int(buf[0]) == FRP_TLS_HEAD_BYTE {
 | 
			
		||||
		out = tls.Server(c, tlsConfig)
 | 
			
		||||
	} else {
 | 
			
		||||
		if tlsOnly {
 | 
			
		||||
			err = fmt.Errorf("non-TLS connection received on a TlsOnly server")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		out = sc
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								utils/tcpmux/httpconnect.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								utils/tcpmux/httpconnect.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
// Copyright 2020 guylewin, guy@lewin.co.il
 | 
			
		||||
//
 | 
			
		||||
// 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 tcpmux
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
	"github.com/fatedier/frp/utils/vhost"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type HttpConnectTcpMuxer struct {
 | 
			
		||||
	*vhost.VhostMuxer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHttpConnectTcpMuxer(listener net.Listener, timeout time.Duration) (*HttpConnectTcpMuxer, error) {
 | 
			
		||||
	mux, err := vhost.NewVhostMuxer(listener, getHostFromHttpConnect, nil, sendHttpOk, nil, timeout)
 | 
			
		||||
	return &HttpConnectTcpMuxer{mux}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readHttpConnectRequest(rd io.Reader) (host string, err error) {
 | 
			
		||||
	bufioReader := bufio.NewReader(rd)
 | 
			
		||||
 | 
			
		||||
	req, err := http.ReadRequest(bufioReader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if req.Method != "CONNECT" {
 | 
			
		||||
		err = fmt.Errorf("connections to tcp vhost must be of method CONNECT")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host = util.GetHostFromAddr(req.Host)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sendHttpOk(c net.Conn) error {
 | 
			
		||||
	return util.OkResponse().Write(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getHostFromHttpConnect(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
 | 
			
		||||
	reqInfoMap := make(map[string]string, 0)
 | 
			
		||||
	host, err := readHttpConnectRequest(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, reqInfoMap, err
 | 
			
		||||
	}
 | 
			
		||||
	reqInfoMap["Host"] = host
 | 
			
		||||
	reqInfoMap["Scheme"] = "tcp"
 | 
			
		||||
	return c, reqInfoMap, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								utils/util/http.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								utils/util/http.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
// Copyright 2020 guylewin, guy@lewin.co.il
 | 
			
		||||
//
 | 
			
		||||
// 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 util
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func OkResponse() *http.Response {
 | 
			
		||||
	header := make(http.Header)
 | 
			
		||||
 | 
			
		||||
	res := &http.Response{
 | 
			
		||||
		Status:     "OK",
 | 
			
		||||
		StatusCode: 200,
 | 
			
		||||
		Proto:      "HTTP/1.1",
 | 
			
		||||
		ProtoMajor: 1,
 | 
			
		||||
		ProtoMinor: 1,
 | 
			
		||||
		Header:     header,
 | 
			
		||||
	}
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetHostFromAddr(addr string) (host string) {
 | 
			
		||||
	strs := strings.Split(addr, ":")
 | 
			
		||||
	if len(strs) > 1 {
 | 
			
		||||
		host = strs[0]
 | 
			
		||||
	} else {
 | 
			
		||||
		host = addr
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -101,3 +101,11 @@ func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) {
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GenerateResponseErrorString(summary string, err error, detailed bool) string {
 | 
			
		||||
	if detailed {
 | 
			
		||||
		return err.Error()
 | 
			
		||||
	} else {
 | 
			
		||||
		return summary
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var version string = "0.31.2"
 | 
			
		||||
var version string = "0.32.0"
 | 
			
		||||
 | 
			
		||||
func Full() string {
 | 
			
		||||
	return version
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	frpLog "github.com/fatedier/frp/utils/log"
 | 
			
		||||
	"github.com/fatedier/frp/utils/util"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/golib/pool"
 | 
			
		||||
)
 | 
			
		||||
@@ -34,16 +35,6 @@ var (
 | 
			
		||||
	ErrNoDomain = errors.New("no such domain")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getHostFromAddr(addr string) (host string) {
 | 
			
		||||
	strs := strings.Split(addr, ":")
 | 
			
		||||
	if len(strs) > 1 {
 | 
			
		||||
		host = strs[0]
 | 
			
		||||
	} else {
 | 
			
		||||
		host = addr
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type HttpReverseProxyOptions struct {
 | 
			
		||||
	ResponseHeaderTimeoutS int64
 | 
			
		||||
}
 | 
			
		||||
@@ -67,7 +58,7 @@ func NewHttpReverseProxy(option HttpReverseProxyOptions, vhostRouter *VhostRoute
 | 
			
		||||
		Director: func(req *http.Request) {
 | 
			
		||||
			req.URL.Scheme = "http"
 | 
			
		||||
			url := req.Context().Value("url").(string)
 | 
			
		||||
			oldHost := getHostFromAddr(req.Context().Value("host").(string))
 | 
			
		||||
			oldHost := util.GetHostFromAddr(req.Context().Value("host").(string))
 | 
			
		||||
			host := rp.GetRealHost(oldHost, url)
 | 
			
		||||
			if host != "" {
 | 
			
		||||
				req.Host = host
 | 
			
		||||
@@ -84,7 +75,7 @@ func NewHttpReverseProxy(option HttpReverseProxyOptions, vhostRouter *VhostRoute
 | 
			
		||||
			DisableKeepAlives:     true,
 | 
			
		||||
			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
 | 
			
		||||
				url := ctx.Value("url").(string)
 | 
			
		||||
				host := getHostFromAddr(ctx.Value("host").(string))
 | 
			
		||||
				host := util.GetHostFromAddr(ctx.Value("host").(string))
 | 
			
		||||
				remote := ctx.Value("remote").(string)
 | 
			
		||||
				return rp.CreateConnection(host, url, remote)
 | 
			
		||||
			},
 | 
			
		||||
@@ -183,11 +174,10 @@ func (rp *HttpReverseProxy) getVhost(domain string, location string) (vr *VhostR
 | 
			
		||||
		}
 | 
			
		||||
		domainSplit = domainSplit[1:]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rp *HttpReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	domain := getHostFromAddr(req.Host)
 | 
			
		||||
	domain := util.GetHostFromAddr(req.Host)
 | 
			
		||||
	location := req.URL.Path
 | 
			
		||||
	user, passwd, _ := req.BasicAuth()
 | 
			
		||||
	if !rp.CheckAuth(domain, location, user, passwd) {
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ type HttpsMuxer struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHttpsMuxer(listener net.Listener, timeout time.Duration) (*HttpsMuxer, error) {
 | 
			
		||||
	mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, nil, timeout)
 | 
			
		||||
	mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, nil, nil, timeout)
 | 
			
		||||
	return &HttpsMuxer{mux}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,22 +29,25 @@ import (
 | 
			
		||||
type muxFunc func(net.Conn) (net.Conn, map[string]string, error)
 | 
			
		||||
type httpAuthFunc func(net.Conn, string, string, string) (bool, error)
 | 
			
		||||
type hostRewriteFunc func(net.Conn, string) (net.Conn, error)
 | 
			
		||||
type successFunc func(net.Conn) error
 | 
			
		||||
 | 
			
		||||
type VhostMuxer struct {
 | 
			
		||||
	listener       net.Listener
 | 
			
		||||
	timeout        time.Duration
 | 
			
		||||
	vhostFunc      muxFunc
 | 
			
		||||
	authFunc       httpAuthFunc
 | 
			
		||||
	successFunc    successFunc
 | 
			
		||||
	rewriteFunc    hostRewriteFunc
 | 
			
		||||
	registryRouter *VhostRouters
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
 | 
			
		||||
func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, successFunc successFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
 | 
			
		||||
	mux = &VhostMuxer{
 | 
			
		||||
		listener:       listener,
 | 
			
		||||
		timeout:        timeout,
 | 
			
		||||
		vhostFunc:      vhostFunc,
 | 
			
		||||
		authFunc:       authFunc,
 | 
			
		||||
		successFunc:    successFunc,
 | 
			
		||||
		rewriteFunc:    rewriteFunc,
 | 
			
		||||
		registryRouter: NewVhostRouters(),
 | 
			
		||||
	}
 | 
			
		||||
@@ -113,7 +116,6 @@ func (v *VhostMuxer) getListener(name, path string) (l *Listener, exist bool) {
 | 
			
		||||
		}
 | 
			
		||||
		domainSplit = domainSplit[1:]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *VhostMuxer) run() {
 | 
			
		||||
@@ -149,7 +151,15 @@ func (v *VhostMuxer) handle(c net.Conn) {
 | 
			
		||||
		c.Close()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xl := xlog.FromContextSafe(l.ctx)
 | 
			
		||||
	if v.successFunc != nil {
 | 
			
		||||
		if err := v.successFunc(c); err != nil {
 | 
			
		||||
			xl.Info("success func failure on vhost connection: %v", err)
 | 
			
		||||
			c.Close()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if authFunc is exist and userName/password is set
 | 
			
		||||
	// then verify user access
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/armon/go-socks5/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/armon/go-socks5/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,22 +0,0 @@
 | 
			
		||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
 | 
			
		||||
*.o
 | 
			
		||||
*.a
 | 
			
		||||
*.so
 | 
			
		||||
 | 
			
		||||
# Folders
 | 
			
		||||
_obj
 | 
			
		||||
_test
 | 
			
		||||
 | 
			
		||||
# Architecture specific extensions/prefixes
 | 
			
		||||
*.[568vq]
 | 
			
		||||
[568vq].out
 | 
			
		||||
 | 
			
		||||
*.cgo1.go
 | 
			
		||||
*.cgo2.c
 | 
			
		||||
_cgo_defun.c
 | 
			
		||||
_cgo_gotypes.go
 | 
			
		||||
_cgo_export.*
 | 
			
		||||
 | 
			
		||||
_testmain.go
 | 
			
		||||
 | 
			
		||||
*.exe
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/armon/go-socks5/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/armon/go-socks5/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
language: go
 | 
			
		||||
go:
 | 
			
		||||
  - 1.1
 | 
			
		||||
  - tip
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/armon/go-socks5/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/armon/go-socks5/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,20 +0,0 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2014 Armon Dadgar
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										45
									
								
								vendor/github.com/armon/go-socks5/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										45
									
								
								vendor/github.com/armon/go-socks5/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,45 +0,0 @@
 | 
			
		||||
go-socks5 [](https://travis-ci.org/armon/go-socks5)
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
Provides the `socks5` package that implements a [SOCKS5 server](http://en.wikipedia.org/wiki/SOCKS).
 | 
			
		||||
SOCKS (Secure Sockets) is used to route traffic between a client and server through
 | 
			
		||||
an intermediate proxy layer. This can be used to bypass firewalls or NATs.
 | 
			
		||||
 | 
			
		||||
Feature
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
The package has the following features:
 | 
			
		||||
* "No Auth" mode
 | 
			
		||||
* User/Password authentication
 | 
			
		||||
* Support for the CONNECT command
 | 
			
		||||
* Rules to do granular filtering of commands
 | 
			
		||||
* Custom DNS resolution
 | 
			
		||||
* Unit tests
 | 
			
		||||
 | 
			
		||||
TODO
 | 
			
		||||
====
 | 
			
		||||
 | 
			
		||||
The package still needs the following:
 | 
			
		||||
* Support for the BIND command
 | 
			
		||||
* Support for the ASSOCIATE command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
Below is a simple example of usage
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// Create a SOCKS5 server
 | 
			
		||||
conf := &socks5.Config{}
 | 
			
		||||
server, err := socks5.New(conf)
 | 
			
		||||
if err != nil {
 | 
			
		||||
  panic(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create SOCKS5 proxy on localhost port 8000
 | 
			
		||||
if err := server.ListenAndServe("tcp", "127.0.0.1:8000"); err != nil {
 | 
			
		||||
  panic(err)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										151
									
								
								vendor/github.com/armon/go-socks5/auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										151
									
								
								vendor/github.com/armon/go-socks5/auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,151 +0,0 @@
 | 
			
		||||
package socks5
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	NoAuth          = uint8(0)
 | 
			
		||||
	noAcceptable    = uint8(255)
 | 
			
		||||
	UserPassAuth    = uint8(2)
 | 
			
		||||
	userAuthVersion = uint8(1)
 | 
			
		||||
	authSuccess     = uint8(0)
 | 
			
		||||
	authFailure     = uint8(1)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	UserAuthFailed  = fmt.Errorf("User authentication failed")
 | 
			
		||||
	NoSupportedAuth = fmt.Errorf("No supported authentication mechanism")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Request encapsulates authentication state provided
 | 
			
		||||
// during negotiation
 | 
			
		||||
type AuthContext struct {
 | 
			
		||||
	// Provided auth method
 | 
			
		||||
	Method uint8
 | 
			
		||||
	// Payload provided during negotiation.
 | 
			
		||||
	// Keys depend on the used auth method.
 | 
			
		||||
	// For UserPassauth contains Username
 | 
			
		||||
	Payload map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Authenticator interface {
 | 
			
		||||
	Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error)
 | 
			
		||||
	GetCode() uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NoAuthAuthenticator is used to handle the "No Authentication" mode
 | 
			
		||||
type NoAuthAuthenticator struct{}
 | 
			
		||||
 | 
			
		||||
func (a NoAuthAuthenticator) GetCode() uint8 {
 | 
			
		||||
	return NoAuth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a NoAuthAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) {
 | 
			
		||||
	_, err := writer.Write([]byte{socks5Version, NoAuth})
 | 
			
		||||
	return &AuthContext{NoAuth, nil}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserPassAuthenticator is used to handle username/password based
 | 
			
		||||
// authentication
 | 
			
		||||
type UserPassAuthenticator struct {
 | 
			
		||||
	Credentials CredentialStore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a UserPassAuthenticator) GetCode() uint8 {
 | 
			
		||||
	return UserPassAuth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a UserPassAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) {
 | 
			
		||||
	// Tell the client to use user/pass auth
 | 
			
		||||
	if _, err := writer.Write([]byte{socks5Version, UserPassAuth}); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get the version and username length
 | 
			
		||||
	header := []byte{0, 0}
 | 
			
		||||
	if _, err := io.ReadAtLeast(reader, header, 2); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure we are compatible
 | 
			
		||||
	if header[0] != userAuthVersion {
 | 
			
		||||
		return nil, fmt.Errorf("Unsupported auth version: %v", header[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get the user name
 | 
			
		||||
	userLen := int(header[1])
 | 
			
		||||
	user := make([]byte, userLen)
 | 
			
		||||
	if _, err := io.ReadAtLeast(reader, user, userLen); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get the password length
 | 
			
		||||
	if _, err := reader.Read(header[:1]); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get the password
 | 
			
		||||
	passLen := int(header[0])
 | 
			
		||||
	pass := make([]byte, passLen)
 | 
			
		||||
	if _, err := io.ReadAtLeast(reader, pass, passLen); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Verify the password
 | 
			
		||||
	if a.Credentials.Valid(string(user), string(pass)) {
 | 
			
		||||
		if _, err := writer.Write([]byte{userAuthVersion, authSuccess}); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if _, err := writer.Write([]byte{userAuthVersion, authFailure}); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return nil, UserAuthFailed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Done
 | 
			
		||||
	return &AuthContext{UserPassAuth, map[string]string{"Username": string(user)}}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// authenticate is used to handle connection authentication
 | 
			
		||||
func (s *Server) authenticate(conn io.Writer, bufConn io.Reader) (*AuthContext, error) {
 | 
			
		||||
	// Get the methods
 | 
			
		||||
	methods, err := readMethods(bufConn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to get auth methods: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Select a usable method
 | 
			
		||||
	for _, method := range methods {
 | 
			
		||||
		cator, found := s.authMethods[method]
 | 
			
		||||
		if found {
 | 
			
		||||
			return cator.Authenticate(bufConn, conn)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No usable method found
 | 
			
		||||
	return nil, noAcceptableAuth(conn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// noAcceptableAuth is used to handle when we have no eligible
 | 
			
		||||
// authentication mechanism
 | 
			
		||||
func noAcceptableAuth(conn io.Writer) error {
 | 
			
		||||
	conn.Write([]byte{socks5Version, noAcceptable})
 | 
			
		||||
	return NoSupportedAuth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMethods is used to read the number of methods
 | 
			
		||||
// and proceeding auth methods
 | 
			
		||||
func readMethods(r io.Reader) ([]byte, error) {
 | 
			
		||||
	header := []byte{0}
 | 
			
		||||
	if _, err := r.Read(header); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	numMethods := int(header[0])
 | 
			
		||||
	methods := make([]byte, numMethods)
 | 
			
		||||
	_, err := io.ReadAtLeast(r, methods, numMethods)
 | 
			
		||||
	return methods, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/armon/go-socks5/credentials.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/armon/go-socks5/credentials.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,17 +0,0 @@
 | 
			
		||||
package socks5
 | 
			
		||||
 | 
			
		||||
// CredentialStore is used to support user/pass authentication
 | 
			
		||||
type CredentialStore interface {
 | 
			
		||||
	Valid(user, password string) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StaticCredentials enables using a map directly as a credential store
 | 
			
		||||
type StaticCredentials map[string]string
 | 
			
		||||
 | 
			
		||||
func (s StaticCredentials) Valid(user, password string) bool {
 | 
			
		||||
	pass, ok := s[user]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return password == pass
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										364
									
								
								vendor/github.com/armon/go-socks5/request.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										364
									
								
								vendor/github.com/armon/go-socks5/request.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,364 +0,0 @@
 | 
			
		||||
package socks5
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ConnectCommand   = uint8(1)
 | 
			
		||||
	BindCommand      = uint8(2)
 | 
			
		||||
	AssociateCommand = uint8(3)
 | 
			
		||||
	ipv4Address      = uint8(1)
 | 
			
		||||
	fqdnAddress      = uint8(3)
 | 
			
		||||
	ipv6Address      = uint8(4)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	successReply uint8 = iota
 | 
			
		||||
	serverFailure
 | 
			
		||||
	ruleFailure
 | 
			
		||||
	networkUnreachable
 | 
			
		||||
	hostUnreachable
 | 
			
		||||
	connectionRefused
 | 
			
		||||
	ttlExpired
 | 
			
		||||
	commandNotSupported
 | 
			
		||||
	addrTypeNotSupported
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	unrecognizedAddrType = fmt.Errorf("Unrecognized address type")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AddressRewriter is used to rewrite a destination transparently
 | 
			
		||||
type AddressRewriter interface {
 | 
			
		||||
	Rewrite(ctx context.Context, request *Request) (context.Context, *AddrSpec)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddrSpec is used to return the target AddrSpec
 | 
			
		||||
// which may be specified as IPv4, IPv6, or a FQDN
 | 
			
		||||
type AddrSpec struct {
 | 
			
		||||
	FQDN string
 | 
			
		||||
	IP   net.IP
 | 
			
		||||
	Port int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *AddrSpec) String() string {
 | 
			
		||||
	if a.FQDN != "" {
 | 
			
		||||
		return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s:%d", a.IP, a.Port)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Address returns a string suitable to dial; prefer returning IP-based
 | 
			
		||||
// address, fallback to FQDN
 | 
			
		||||
func (a AddrSpec) Address() string {
 | 
			
		||||
	if 0 != len(a.IP) {
 | 
			
		||||
		return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
 | 
			
		||||
	}
 | 
			
		||||
	return net.JoinHostPort(a.FQDN, strconv.Itoa(a.Port))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Request represents request received by a server
 | 
			
		||||
type Request struct {
 | 
			
		||||
	// Protocol version
 | 
			
		||||
	Version uint8
 | 
			
		||||
	// Requested command
 | 
			
		||||
	Command uint8
 | 
			
		||||
	// AuthContext provided during negotiation
 | 
			
		||||
	AuthContext *AuthContext
 | 
			
		||||
	// AddrSpec of the the network that sent the request
 | 
			
		||||
	RemoteAddr *AddrSpec
 | 
			
		||||
	// AddrSpec of the desired destination
 | 
			
		||||
	DestAddr *AddrSpec
 | 
			
		||||
	// AddrSpec of the actual destination (might be affected by rewrite)
 | 
			
		||||
	realDestAddr *AddrSpec
 | 
			
		||||
	bufConn      io.Reader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type conn interface {
 | 
			
		||||
	Write([]byte) (int, error)
 | 
			
		||||
	RemoteAddr() net.Addr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRequest creates a new Request from the tcp connection
 | 
			
		||||
func NewRequest(bufConn io.Reader) (*Request, error) {
 | 
			
		||||
	// Read the version byte
 | 
			
		||||
	header := []byte{0, 0, 0}
 | 
			
		||||
	if _, err := io.ReadAtLeast(bufConn, header, 3); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to get command version: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure we are compatible
 | 
			
		||||
	if header[0] != socks5Version {
 | 
			
		||||
		return nil, fmt.Errorf("Unsupported command version: %v", header[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read in the destination address
 | 
			
		||||
	dest, err := readAddrSpec(bufConn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	request := &Request{
 | 
			
		||||
		Version:  socks5Version,
 | 
			
		||||
		Command:  header[1],
 | 
			
		||||
		DestAddr: dest,
 | 
			
		||||
		bufConn:  bufConn,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return request, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleRequest is used for request processing after authentication
 | 
			
		||||
func (s *Server) handleRequest(req *Request, conn conn) error {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
 | 
			
		||||
	// Resolve the address if we have a FQDN
 | 
			
		||||
	dest := req.DestAddr
 | 
			
		||||
	if dest.FQDN != "" {
 | 
			
		||||
		ctx_, addr, err := s.config.Resolver.Resolve(ctx, dest.FQDN)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err := sendReply(conn, hostUnreachable, nil); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("Failed to resolve destination '%v': %v", dest.FQDN, err)
 | 
			
		||||
		}
 | 
			
		||||
		ctx = ctx_
 | 
			
		||||
		dest.IP = addr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Apply any address rewrites
 | 
			
		||||
	req.realDestAddr = req.DestAddr
 | 
			
		||||
	if s.config.Rewriter != nil {
 | 
			
		||||
		ctx, req.realDestAddr = s.config.Rewriter.Rewrite(ctx, req)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Switch on the command
 | 
			
		||||
	switch req.Command {
 | 
			
		||||
	case ConnectCommand:
 | 
			
		||||
		return s.handleConnect(ctx, conn, req)
 | 
			
		||||
	case BindCommand:
 | 
			
		||||
		return s.handleBind(ctx, conn, req)
 | 
			
		||||
	case AssociateCommand:
 | 
			
		||||
		return s.handleAssociate(ctx, conn, req)
 | 
			
		||||
	default:
 | 
			
		||||
		if err := sendReply(conn, commandNotSupported, nil); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("Unsupported command: %v", req.Command)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleConnect is used to handle a connect command
 | 
			
		||||
func (s *Server) handleConnect(ctx context.Context, conn conn, req *Request) error {
 | 
			
		||||
	// Check if this is allowed
 | 
			
		||||
	if ctx_, ok := s.config.Rules.Allow(ctx, req); !ok {
 | 
			
		||||
		if err := sendReply(conn, ruleFailure, nil); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("Connect to %v blocked by rules", req.DestAddr)
 | 
			
		||||
	} else {
 | 
			
		||||
		ctx = ctx_
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Attempt to connect
 | 
			
		||||
	dial := s.config.Dial
 | 
			
		||||
	if dial == nil {
 | 
			
		||||
		dial = func(ctx context.Context, net_, addr string) (net.Conn, error) {
 | 
			
		||||
			return net.Dial(net_, addr)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	target, err := dial(ctx, "tcp", req.realDestAddr.Address())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		msg := err.Error()
 | 
			
		||||
		resp := hostUnreachable
 | 
			
		||||
		if strings.Contains(msg, "refused") {
 | 
			
		||||
			resp = connectionRefused
 | 
			
		||||
		} else if strings.Contains(msg, "network is unreachable") {
 | 
			
		||||
			resp = networkUnreachable
 | 
			
		||||
		}
 | 
			
		||||
		if err := sendReply(conn, resp, nil); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("Connect to %v failed: %v", req.DestAddr, err)
 | 
			
		||||
	}
 | 
			
		||||
	defer target.Close()
 | 
			
		||||
 | 
			
		||||
	// Send success
 | 
			
		||||
	local := target.LocalAddr().(*net.TCPAddr)
 | 
			
		||||
	bind := AddrSpec{IP: local.IP, Port: local.Port}
 | 
			
		||||
	if err := sendReply(conn, successReply, &bind); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Start proxying
 | 
			
		||||
	errCh := make(chan error, 2)
 | 
			
		||||
	go proxy(target, req.bufConn, errCh)
 | 
			
		||||
	go proxy(conn, target, errCh)
 | 
			
		||||
 | 
			
		||||
	// Wait
 | 
			
		||||
	for i := 0; i < 2; i++ {
 | 
			
		||||
		e := <-errCh
 | 
			
		||||
		if e != nil {
 | 
			
		||||
			// return from this function closes target (and conn).
 | 
			
		||||
			return e
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleBind is used to handle a connect command
 | 
			
		||||
func (s *Server) handleBind(ctx context.Context, conn conn, req *Request) error {
 | 
			
		||||
	// Check if this is allowed
 | 
			
		||||
	if ctx_, ok := s.config.Rules.Allow(ctx, req); !ok {
 | 
			
		||||
		if err := sendReply(conn, ruleFailure, nil); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("Bind to %v blocked by rules", req.DestAddr)
 | 
			
		||||
	} else {
 | 
			
		||||
		ctx = ctx_
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: Support bind
 | 
			
		||||
	if err := sendReply(conn, commandNotSupported, nil); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleAssociate is used to handle a connect command
 | 
			
		||||
func (s *Server) handleAssociate(ctx context.Context, conn conn, req *Request) error {
 | 
			
		||||
	// Check if this is allowed
 | 
			
		||||
	if ctx_, ok := s.config.Rules.Allow(ctx, req); !ok {
 | 
			
		||||
		if err := sendReply(conn, ruleFailure, nil); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("Associate to %v blocked by rules", req.DestAddr)
 | 
			
		||||
	} else {
 | 
			
		||||
		ctx = ctx_
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: Support associate
 | 
			
		||||
	if err := sendReply(conn, commandNotSupported, nil); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readAddrSpec is used to read AddrSpec.
 | 
			
		||||
// Expects an address type byte, follwed by the address and port
 | 
			
		||||
func readAddrSpec(r io.Reader) (*AddrSpec, error) {
 | 
			
		||||
	d := &AddrSpec{}
 | 
			
		||||
 | 
			
		||||
	// Get the address type
 | 
			
		||||
	addrType := []byte{0}
 | 
			
		||||
	if _, err := r.Read(addrType); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle on a per type basis
 | 
			
		||||
	switch addrType[0] {
 | 
			
		||||
	case ipv4Address:
 | 
			
		||||
		addr := make([]byte, 4)
 | 
			
		||||
		if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		d.IP = net.IP(addr)
 | 
			
		||||
 | 
			
		||||
	case ipv6Address:
 | 
			
		||||
		addr := make([]byte, 16)
 | 
			
		||||
		if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		d.IP = net.IP(addr)
 | 
			
		||||
 | 
			
		||||
	case fqdnAddress:
 | 
			
		||||
		if _, err := r.Read(addrType); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		addrLen := int(addrType[0])
 | 
			
		||||
		fqdn := make([]byte, addrLen)
 | 
			
		||||
		if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		d.FQDN = string(fqdn)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, unrecognizedAddrType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read the port
 | 
			
		||||
	port := []byte{0, 0}
 | 
			
		||||
	if _, err := io.ReadAtLeast(r, port, 2); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	d.Port = (int(port[0]) << 8) | int(port[1])
 | 
			
		||||
 | 
			
		||||
	return d, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendReply is used to send a reply message
 | 
			
		||||
func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error {
 | 
			
		||||
	// Format the address
 | 
			
		||||
	var addrType uint8
 | 
			
		||||
	var addrBody []byte
 | 
			
		||||
	var addrPort uint16
 | 
			
		||||
	switch {
 | 
			
		||||
	case addr == nil:
 | 
			
		||||
		addrType = ipv4Address
 | 
			
		||||
		addrBody = []byte{0, 0, 0, 0}
 | 
			
		||||
		addrPort = 0
 | 
			
		||||
 | 
			
		||||
	case addr.FQDN != "":
 | 
			
		||||
		addrType = fqdnAddress
 | 
			
		||||
		addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...)
 | 
			
		||||
		addrPort = uint16(addr.Port)
 | 
			
		||||
 | 
			
		||||
	case addr.IP.To4() != nil:
 | 
			
		||||
		addrType = ipv4Address
 | 
			
		||||
		addrBody = []byte(addr.IP.To4())
 | 
			
		||||
		addrPort = uint16(addr.Port)
 | 
			
		||||
 | 
			
		||||
	case addr.IP.To16() != nil:
 | 
			
		||||
		addrType = ipv6Address
 | 
			
		||||
		addrBody = []byte(addr.IP.To16())
 | 
			
		||||
		addrPort = uint16(addr.Port)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("Failed to format address: %v", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Format the message
 | 
			
		||||
	msg := make([]byte, 6+len(addrBody))
 | 
			
		||||
	msg[0] = socks5Version
 | 
			
		||||
	msg[1] = resp
 | 
			
		||||
	msg[2] = 0 // Reserved
 | 
			
		||||
	msg[3] = addrType
 | 
			
		||||
	copy(msg[4:], addrBody)
 | 
			
		||||
	msg[4+len(addrBody)] = byte(addrPort >> 8)
 | 
			
		||||
	msg[4+len(addrBody)+1] = byte(addrPort & 0xff)
 | 
			
		||||
 | 
			
		||||
	// Send the message
 | 
			
		||||
	_, err := w.Write(msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type closeWriter interface {
 | 
			
		||||
	CloseWrite() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// proxy is used to suffle data from src to destination, and sends errors
 | 
			
		||||
// down a dedicated channel
 | 
			
		||||
func proxy(dst io.Writer, src io.Reader, errCh chan error) {
 | 
			
		||||
	_, err := io.Copy(dst, src)
 | 
			
		||||
	if tcpConn, ok := dst.(closeWriter); ok {
 | 
			
		||||
		tcpConn.CloseWrite()
 | 
			
		||||
	}
 | 
			
		||||
	errCh <- err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/armon/go-socks5/resolver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/armon/go-socks5/resolver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,23 +0,0 @@
 | 
			
		||||
package socks5
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NameResolver is used to implement custom name resolution
 | 
			
		||||
type NameResolver interface {
 | 
			
		||||
	Resolve(ctx context.Context, name string) (context.Context, net.IP, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DNSResolver uses the system DNS to resolve host names
 | 
			
		||||
type DNSResolver struct{}
 | 
			
		||||
 | 
			
		||||
func (d DNSResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
 | 
			
		||||
	addr, err := net.ResolveIPAddr("ip", name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ctx, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return ctx, addr.IP, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								vendor/github.com/armon/go-socks5/ruleset.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/armon/go-socks5/ruleset.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,41 +0,0 @@
 | 
			
		||||
package socks5
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RuleSet is used to provide custom rules to allow or prohibit actions
 | 
			
		||||
type RuleSet interface {
 | 
			
		||||
	Allow(ctx context.Context, req *Request) (context.Context, bool)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PermitAll returns a RuleSet which allows all types of connections
 | 
			
		||||
func PermitAll() RuleSet {
 | 
			
		||||
	return &PermitCommand{true, true, true}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PermitNone returns a RuleSet which disallows all types of connections
 | 
			
		||||
func PermitNone() RuleSet {
 | 
			
		||||
	return &PermitCommand{false, false, false}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PermitCommand is an implementation of the RuleSet which
 | 
			
		||||
// enables filtering supported commands
 | 
			
		||||
type PermitCommand struct {
 | 
			
		||||
	EnableConnect   bool
 | 
			
		||||
	EnableBind      bool
 | 
			
		||||
	EnableAssociate bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *PermitCommand) Allow(ctx context.Context, req *Request) (context.Context, bool) {
 | 
			
		||||
	switch req.Command {
 | 
			
		||||
	case ConnectCommand:
 | 
			
		||||
		return ctx, p.EnableConnect
 | 
			
		||||
	case BindCommand:
 | 
			
		||||
		return ctx, p.EnableBind
 | 
			
		||||
	case AssociateCommand:
 | 
			
		||||
		return ctx, p.EnableAssociate
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ctx, false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										169
									
								
								vendor/github.com/armon/go-socks5/socks5.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/armon/go-socks5/socks5.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,169 +0,0 @@
 | 
			
		||||
package socks5
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	socks5Version = uint8(5)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Config is used to setup and configure a Server
 | 
			
		||||
type Config struct {
 | 
			
		||||
	// AuthMethods can be provided to implement custom authentication
 | 
			
		||||
	// By default, "auth-less" mode is enabled.
 | 
			
		||||
	// For password-based auth use UserPassAuthenticator.
 | 
			
		||||
	AuthMethods []Authenticator
 | 
			
		||||
 | 
			
		||||
	// If provided, username/password authentication is enabled,
 | 
			
		||||
	// by appending a UserPassAuthenticator to AuthMethods. If not provided,
 | 
			
		||||
	// and AUthMethods is nil, then "auth-less" mode is enabled.
 | 
			
		||||
	Credentials CredentialStore
 | 
			
		||||
 | 
			
		||||
	// Resolver can be provided to do custom name resolution.
 | 
			
		||||
	// Defaults to DNSResolver if not provided.
 | 
			
		||||
	Resolver NameResolver
 | 
			
		||||
 | 
			
		||||
	// Rules is provided to enable custom logic around permitting
 | 
			
		||||
	// various commands. If not provided, PermitAll is used.
 | 
			
		||||
	Rules RuleSet
 | 
			
		||||
 | 
			
		||||
	// Rewriter can be used to transparently rewrite addresses.
 | 
			
		||||
	// This is invoked before the RuleSet is invoked.
 | 
			
		||||
	// Defaults to NoRewrite.
 | 
			
		||||
	Rewriter AddressRewriter
 | 
			
		||||
 | 
			
		||||
	// BindIP is used for bind or udp associate
 | 
			
		||||
	BindIP net.IP
 | 
			
		||||
 | 
			
		||||
	// Logger can be used to provide a custom log target.
 | 
			
		||||
	// Defaults to stdout.
 | 
			
		||||
	Logger *log.Logger
 | 
			
		||||
 | 
			
		||||
	// Optional function for dialing out
 | 
			
		||||
	Dial func(ctx context.Context, network, addr string) (net.Conn, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Server is reponsible for accepting connections and handling
 | 
			
		||||
// the details of the SOCKS5 protocol
 | 
			
		||||
type Server struct {
 | 
			
		||||
	config      *Config
 | 
			
		||||
	authMethods map[uint8]Authenticator
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates a new Server and potentially returns an error
 | 
			
		||||
func New(conf *Config) (*Server, error) {
 | 
			
		||||
	// Ensure we have at least one authentication method enabled
 | 
			
		||||
	if len(conf.AuthMethods) == 0 {
 | 
			
		||||
		if conf.Credentials != nil {
 | 
			
		||||
			conf.AuthMethods = []Authenticator{&UserPassAuthenticator{conf.Credentials}}
 | 
			
		||||
		} else {
 | 
			
		||||
			conf.AuthMethods = []Authenticator{&NoAuthAuthenticator{}}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure we have a DNS resolver
 | 
			
		||||
	if conf.Resolver == nil {
 | 
			
		||||
		conf.Resolver = DNSResolver{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure we have a rule set
 | 
			
		||||
	if conf.Rules == nil {
 | 
			
		||||
		conf.Rules = PermitAll()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure we have a log target
 | 
			
		||||
	if conf.Logger == nil {
 | 
			
		||||
		conf.Logger = log.New(os.Stdout, "", log.LstdFlags)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server := &Server{
 | 
			
		||||
		config: conf,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server.authMethods = make(map[uint8]Authenticator)
 | 
			
		||||
 | 
			
		||||
	for _, a := range conf.AuthMethods {
 | 
			
		||||
		server.authMethods[a.GetCode()] = a
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return server, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenAndServe is used to create a listener and serve on it
 | 
			
		||||
func (s *Server) ListenAndServe(network, addr string) error {
 | 
			
		||||
	l, err := net.Listen(network, addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.Serve(l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serve is used to serve connections from a listener
 | 
			
		||||
func (s *Server) Serve(l net.Listener) error {
 | 
			
		||||
	for {
 | 
			
		||||
		conn, err := l.Accept()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		go s.ServeConn(conn)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServeConn is used to serve a single connection.
 | 
			
		||||
func (s *Server) ServeConn(conn net.Conn) error {
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
	bufConn := bufio.NewReader(conn)
 | 
			
		||||
 | 
			
		||||
	// Read the version byte
 | 
			
		||||
	version := []byte{0}
 | 
			
		||||
	if _, err := bufConn.Read(version); err != nil {
 | 
			
		||||
		s.config.Logger.Printf("[ERR] socks: Failed to get version byte: %v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ensure we are compatible
 | 
			
		||||
	if version[0] != socks5Version {
 | 
			
		||||
		err := fmt.Errorf("Unsupported SOCKS version: %v", version)
 | 
			
		||||
		s.config.Logger.Printf("[ERR] socks: %v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Authenticate the connection
 | 
			
		||||
	authContext, err := s.authenticate(conn, bufConn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Failed to authenticate: %v", err)
 | 
			
		||||
		s.config.Logger.Printf("[ERR] socks: %v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	request, err := NewRequest(bufConn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err == unrecognizedAddrType {
 | 
			
		||||
			if err := sendReply(conn, addrTypeNotSupported, nil); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to send reply: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("Failed to read destination address: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	request.AuthContext = authContext
 | 
			
		||||
	if client, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
 | 
			
		||||
		request.RemoteAddr = &AddrSpec{IP: client.IP, Port: client.Port}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Process the client request
 | 
			
		||||
	if err := s.handleRequest(request, conn); err != nil {
 | 
			
		||||
		err = fmt.Errorf("Failed to handle request: %v", err)
 | 
			
		||||
		s.config.Logger.Printf("[ERR] socks: %v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								vendor/github.com/davecgh/go-spew/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/davecgh/go-spew/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,15 +0,0 @@
 | 
			
		||||
ISC License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
 | 
			
		||||
Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
							
								
								
									
										152
									
								
								vendor/github.com/davecgh/go-spew/spew/bypass.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										152
									
								
								vendor/github.com/davecgh/go-spew/spew/bypass.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,152 +0,0 @@
 | 
			
		||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
//
 | 
			
		||||
// Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
// purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
// copyright notice and this permission notice appear in all copies.
 | 
			
		||||
//
 | 
			
		||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 | 
			
		||||
// NOTE: Due to the following build constraints, this file will only be compiled
 | 
			
		||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
 | 
			
		||||
// "-tags safe" is not added to the go build command line.  The "disableunsafe"
 | 
			
		||||
// tag is deprecated and thus should not be used.
 | 
			
		||||
// +build !js,!appengine,!safe,!disableunsafe
 | 
			
		||||
 | 
			
		||||
package spew
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// UnsafeDisabled is a build-time constant which specifies whether or
 | 
			
		||||
	// not access to the unsafe package is available.
 | 
			
		||||
	UnsafeDisabled = false
 | 
			
		||||
 | 
			
		||||
	// ptrSize is the size of a pointer on the current arch.
 | 
			
		||||
	ptrSize = unsafe.Sizeof((*byte)(nil))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
 | 
			
		||||
	// internal reflect.Value fields.  These values are valid before golang
 | 
			
		||||
	// commit ecccf07e7f9d which changed the format.  The are also valid
 | 
			
		||||
	// after commit 82f48826c6c7 which changed the format again to mirror
 | 
			
		||||
	// the original format.  Code in the init function updates these offsets
 | 
			
		||||
	// as necessary.
 | 
			
		||||
	offsetPtr    = uintptr(ptrSize)
 | 
			
		||||
	offsetScalar = uintptr(0)
 | 
			
		||||
	offsetFlag   = uintptr(ptrSize * 2)
 | 
			
		||||
 | 
			
		||||
	// flagKindWidth and flagKindShift indicate various bits that the
 | 
			
		||||
	// reflect package uses internally to track kind information.
 | 
			
		||||
	//
 | 
			
		||||
	// flagRO indicates whether or not the value field of a reflect.Value is
 | 
			
		||||
	// read-only.
 | 
			
		||||
	//
 | 
			
		||||
	// flagIndir indicates whether the value field of a reflect.Value is
 | 
			
		||||
	// the actual data or a pointer to the data.
 | 
			
		||||
	//
 | 
			
		||||
	// These values are valid before golang commit 90a7c3c86944 which
 | 
			
		||||
	// changed their positions.  Code in the init function updates these
 | 
			
		||||
	// flags as necessary.
 | 
			
		||||
	flagKindWidth = uintptr(5)
 | 
			
		||||
	flagKindShift = uintptr(flagKindWidth - 1)
 | 
			
		||||
	flagRO        = uintptr(1 << 0)
 | 
			
		||||
	flagIndir     = uintptr(1 << 1)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// Older versions of reflect.Value stored small integers directly in the
 | 
			
		||||
	// ptr field (which is named val in the older versions).  Versions
 | 
			
		||||
	// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
 | 
			
		||||
	// scalar for this purpose which unfortunately came before the flag
 | 
			
		||||
	// field, so the offset of the flag field is different for those
 | 
			
		||||
	// versions.
 | 
			
		||||
	//
 | 
			
		||||
	// This code constructs a new reflect.Value from a known small integer
 | 
			
		||||
	// and checks if the size of the reflect.Value struct indicates it has
 | 
			
		||||
	// the scalar field. When it does, the offsets are updated accordingly.
 | 
			
		||||
	vv := reflect.ValueOf(0xf00)
 | 
			
		||||
	if unsafe.Sizeof(vv) == (ptrSize * 4) {
 | 
			
		||||
		offsetScalar = ptrSize * 2
 | 
			
		||||
		offsetFlag = ptrSize * 3
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Commit 90a7c3c86944 changed the flag positions such that the low
 | 
			
		||||
	// order bits are the kind.  This code extracts the kind from the flags
 | 
			
		||||
	// field and ensures it's the correct type.  When it's not, the flag
 | 
			
		||||
	// order has been changed to the newer format, so the flags are updated
 | 
			
		||||
	// accordingly.
 | 
			
		||||
	upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
 | 
			
		||||
	upfv := *(*uintptr)(upf)
 | 
			
		||||
	flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
 | 
			
		||||
	if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
 | 
			
		||||
		flagKindShift = 0
 | 
			
		||||
		flagRO = 1 << 5
 | 
			
		||||
		flagIndir = 1 << 6
 | 
			
		||||
 | 
			
		||||
		// Commit adf9b30e5594 modified the flags to separate the
 | 
			
		||||
		// flagRO flag into two bits which specifies whether or not the
 | 
			
		||||
		// field is embedded.  This causes flagIndir to move over a bit
 | 
			
		||||
		// and means that flagRO is the combination of either of the
 | 
			
		||||
		// original flagRO bit and the new bit.
 | 
			
		||||
		//
 | 
			
		||||
		// This code detects the change by extracting what used to be
 | 
			
		||||
		// the indirect bit to ensure it's set.  When it's not, the flag
 | 
			
		||||
		// order has been changed to the newer format, so the flags are
 | 
			
		||||
		// updated accordingly.
 | 
			
		||||
		if upfv&flagIndir == 0 {
 | 
			
		||||
			flagRO = 3 << 5
 | 
			
		||||
			flagIndir = 1 << 7
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
 | 
			
		||||
// the typical safety restrictions preventing access to unaddressable and
 | 
			
		||||
// unexported data.  It works by digging the raw pointer to the underlying
 | 
			
		||||
// value out of the protected value and generating a new unprotected (unsafe)
 | 
			
		||||
// reflect.Value to it.
 | 
			
		||||
//
 | 
			
		||||
// This allows us to check for implementations of the Stringer and error
 | 
			
		||||
// interfaces to be used for pretty printing ordinarily unaddressable and
 | 
			
		||||
// inaccessible values such as unexported struct fields.
 | 
			
		||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
 | 
			
		||||
	indirects := 1
 | 
			
		||||
	vt := v.Type()
 | 
			
		||||
	upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
 | 
			
		||||
	rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
 | 
			
		||||
	if rvf&flagIndir != 0 {
 | 
			
		||||
		vt = reflect.PtrTo(v.Type())
 | 
			
		||||
		indirects++
 | 
			
		||||
	} else if offsetScalar != 0 {
 | 
			
		||||
		// The value is in the scalar field when it's not one of the
 | 
			
		||||
		// reference types.
 | 
			
		||||
		switch vt.Kind() {
 | 
			
		||||
		case reflect.Uintptr:
 | 
			
		||||
		case reflect.Chan:
 | 
			
		||||
		case reflect.Func:
 | 
			
		||||
		case reflect.Map:
 | 
			
		||||
		case reflect.Ptr:
 | 
			
		||||
		case reflect.UnsafePointer:
 | 
			
		||||
		default:
 | 
			
		||||
			upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
 | 
			
		||||
				offsetScalar)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pv := reflect.NewAt(vt, upv)
 | 
			
		||||
	rv = pv
 | 
			
		||||
	for i := 0; i < indirects; i++ {
 | 
			
		||||
		rv = rv.Elem()
 | 
			
		||||
	}
 | 
			
		||||
	return rv
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,38 +0,0 @@
 | 
			
		||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
//
 | 
			
		||||
// Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
// purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
// copyright notice and this permission notice appear in all copies.
 | 
			
		||||
//
 | 
			
		||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 | 
			
		||||
// NOTE: Due to the following build constraints, this file will only be compiled
 | 
			
		||||
// when the code is running on Google App Engine, compiled by GopherJS, or
 | 
			
		||||
// "-tags safe" is added to the go build command line.  The "disableunsafe"
 | 
			
		||||
// tag is deprecated and thus should not be used.
 | 
			
		||||
// +build js appengine safe disableunsafe
 | 
			
		||||
 | 
			
		||||
package spew
 | 
			
		||||
 | 
			
		||||
import "reflect"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// UnsafeDisabled is a build-time constant which specifies whether or
 | 
			
		||||
	// not access to the unsafe package is available.
 | 
			
		||||
	UnsafeDisabled = true
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
 | 
			
		||||
// that bypasses the typical safety restrictions preventing access to
 | 
			
		||||
// unaddressable and unexported data.  However, doing this relies on access to
 | 
			
		||||
// the unsafe package.  This is a stub version which simply returns the passed
 | 
			
		||||
// reflect.Value when the unsafe package is not available.
 | 
			
		||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										341
									
								
								vendor/github.com/davecgh/go-spew/spew/common.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										341
									
								
								vendor/github.com/davecgh/go-spew/spew/common.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,341 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package spew
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Some constants in the form of bytes to avoid string overhead.  This mirrors
 | 
			
		||||
// the technique used in the fmt package.
 | 
			
		||||
var (
 | 
			
		||||
	panicBytes            = []byte("(PANIC=")
 | 
			
		||||
	plusBytes             = []byte("+")
 | 
			
		||||
	iBytes                = []byte("i")
 | 
			
		||||
	trueBytes             = []byte("true")
 | 
			
		||||
	falseBytes            = []byte("false")
 | 
			
		||||
	interfaceBytes        = []byte("(interface {})")
 | 
			
		||||
	commaNewlineBytes     = []byte(",\n")
 | 
			
		||||
	newlineBytes          = []byte("\n")
 | 
			
		||||
	openBraceBytes        = []byte("{")
 | 
			
		||||
	openBraceNewlineBytes = []byte("{\n")
 | 
			
		||||
	closeBraceBytes       = []byte("}")
 | 
			
		||||
	asteriskBytes         = []byte("*")
 | 
			
		||||
	colonBytes            = []byte(":")
 | 
			
		||||
	colonSpaceBytes       = []byte(": ")
 | 
			
		||||
	openParenBytes        = []byte("(")
 | 
			
		||||
	closeParenBytes       = []byte(")")
 | 
			
		||||
	spaceBytes            = []byte(" ")
 | 
			
		||||
	pointerChainBytes     = []byte("->")
 | 
			
		||||
	nilAngleBytes         = []byte("<nil>")
 | 
			
		||||
	maxNewlineBytes       = []byte("<max depth reached>\n")
 | 
			
		||||
	maxShortBytes         = []byte("<max>")
 | 
			
		||||
	circularBytes         = []byte("<already shown>")
 | 
			
		||||
	circularShortBytes    = []byte("<shown>")
 | 
			
		||||
	invalidAngleBytes     = []byte("<invalid>")
 | 
			
		||||
	openBracketBytes      = []byte("[")
 | 
			
		||||
	closeBracketBytes     = []byte("]")
 | 
			
		||||
	percentBytes          = []byte("%")
 | 
			
		||||
	precisionBytes        = []byte(".")
 | 
			
		||||
	openAngleBytes        = []byte("<")
 | 
			
		||||
	closeAngleBytes       = []byte(">")
 | 
			
		||||
	openMapBytes          = []byte("map[")
 | 
			
		||||
	closeMapBytes         = []byte("]")
 | 
			
		||||
	lenEqualsBytes        = []byte("len=")
 | 
			
		||||
	capEqualsBytes        = []byte("cap=")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// hexDigits is used to map a decimal value to a hex digit.
 | 
			
		||||
var hexDigits = "0123456789abcdef"
 | 
			
		||||
 | 
			
		||||
// catchPanic handles any panics that might occur during the handleMethods
 | 
			
		||||
// calls.
 | 
			
		||||
func catchPanic(w io.Writer, v reflect.Value) {
 | 
			
		||||
	if err := recover(); err != nil {
 | 
			
		||||
		w.Write(panicBytes)
 | 
			
		||||
		fmt.Fprintf(w, "%v", err)
 | 
			
		||||
		w.Write(closeParenBytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleMethods attempts to call the Error and String methods on the underlying
 | 
			
		||||
// type the passed reflect.Value represents and outputes the result to Writer w.
 | 
			
		||||
//
 | 
			
		||||
// It handles panics in any called methods by catching and displaying the error
 | 
			
		||||
// as the formatted value.
 | 
			
		||||
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
 | 
			
		||||
	// We need an interface to check if the type implements the error or
 | 
			
		||||
	// Stringer interface.  However, the reflect package won't give us an
 | 
			
		||||
	// interface on certain things like unexported struct fields in order
 | 
			
		||||
	// to enforce visibility rules.  We use unsafe, when it's available,
 | 
			
		||||
	// to bypass these restrictions since this package does not mutate the
 | 
			
		||||
	// values.
 | 
			
		||||
	if !v.CanInterface() {
 | 
			
		||||
		if UnsafeDisabled {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		v = unsafeReflectValue(v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Choose whether or not to do error and Stringer interface lookups against
 | 
			
		||||
	// the base type or a pointer to the base type depending on settings.
 | 
			
		||||
	// Technically calling one of these methods with a pointer receiver can
 | 
			
		||||
	// mutate the value, however, types which choose to satisify an error or
 | 
			
		||||
	// Stringer interface with a pointer receiver should not be mutating their
 | 
			
		||||
	// state inside these interface methods.
 | 
			
		||||
	if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
 | 
			
		||||
		v = unsafeReflectValue(v)
 | 
			
		||||
	}
 | 
			
		||||
	if v.CanAddr() {
 | 
			
		||||
		v = v.Addr()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Is it an error or Stringer?
 | 
			
		||||
	switch iface := v.Interface().(type) {
 | 
			
		||||
	case error:
 | 
			
		||||
		defer catchPanic(w, v)
 | 
			
		||||
		if cs.ContinueOnMethod {
 | 
			
		||||
			w.Write(openParenBytes)
 | 
			
		||||
			w.Write([]byte(iface.Error()))
 | 
			
		||||
			w.Write(closeParenBytes)
 | 
			
		||||
			w.Write(spaceBytes)
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w.Write([]byte(iface.Error()))
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case fmt.Stringer:
 | 
			
		||||
		defer catchPanic(w, v)
 | 
			
		||||
		if cs.ContinueOnMethod {
 | 
			
		||||
			w.Write(openParenBytes)
 | 
			
		||||
			w.Write([]byte(iface.String()))
 | 
			
		||||
			w.Write(closeParenBytes)
 | 
			
		||||
			w.Write(spaceBytes)
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		w.Write([]byte(iface.String()))
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// printBool outputs a boolean value as true or false to Writer w.
 | 
			
		||||
func printBool(w io.Writer, val bool) {
 | 
			
		||||
	if val {
 | 
			
		||||
		w.Write(trueBytes)
 | 
			
		||||
	} else {
 | 
			
		||||
		w.Write(falseBytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// printInt outputs a signed integer value to Writer w.
 | 
			
		||||
func printInt(w io.Writer, val int64, base int) {
 | 
			
		||||
	w.Write([]byte(strconv.FormatInt(val, base)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// printUint outputs an unsigned integer value to Writer w.
 | 
			
		||||
func printUint(w io.Writer, val uint64, base int) {
 | 
			
		||||
	w.Write([]byte(strconv.FormatUint(val, base)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// printFloat outputs a floating point value using the specified precision,
 | 
			
		||||
// which is expected to be 32 or 64bit, to Writer w.
 | 
			
		||||
func printFloat(w io.Writer, val float64, precision int) {
 | 
			
		||||
	w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// printComplex outputs a complex value using the specified float precision
 | 
			
		||||
// for the real and imaginary parts to Writer w.
 | 
			
		||||
func printComplex(w io.Writer, c complex128, floatPrecision int) {
 | 
			
		||||
	r := real(c)
 | 
			
		||||
	w.Write(openParenBytes)
 | 
			
		||||
	w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
 | 
			
		||||
	i := imag(c)
 | 
			
		||||
	if i >= 0 {
 | 
			
		||||
		w.Write(plusBytes)
 | 
			
		||||
	}
 | 
			
		||||
	w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
 | 
			
		||||
	w.Write(iBytes)
 | 
			
		||||
	w.Write(closeParenBytes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
 | 
			
		||||
// prefix to Writer w.
 | 
			
		||||
func printHexPtr(w io.Writer, p uintptr) {
 | 
			
		||||
	// Null pointer.
 | 
			
		||||
	num := uint64(p)
 | 
			
		||||
	if num == 0 {
 | 
			
		||||
		w.Write(nilAngleBytes)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
 | 
			
		||||
	buf := make([]byte, 18)
 | 
			
		||||
 | 
			
		||||
	// It's simpler to construct the hex string right to left.
 | 
			
		||||
	base := uint64(16)
 | 
			
		||||
	i := len(buf) - 1
 | 
			
		||||
	for num >= base {
 | 
			
		||||
		buf[i] = hexDigits[num%base]
 | 
			
		||||
		num /= base
 | 
			
		||||
		i--
 | 
			
		||||
	}
 | 
			
		||||
	buf[i] = hexDigits[num]
 | 
			
		||||
 | 
			
		||||
	// Add '0x' prefix.
 | 
			
		||||
	i--
 | 
			
		||||
	buf[i] = 'x'
 | 
			
		||||
	i--
 | 
			
		||||
	buf[i] = '0'
 | 
			
		||||
 | 
			
		||||
	// Strip unused leading bytes.
 | 
			
		||||
	buf = buf[i:]
 | 
			
		||||
	w.Write(buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
 | 
			
		||||
// elements to be sorted.
 | 
			
		||||
type valuesSorter struct {
 | 
			
		||||
	values  []reflect.Value
 | 
			
		||||
	strings []string // either nil or same len and values
 | 
			
		||||
	cs      *ConfigState
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newValuesSorter initializes a valuesSorter instance, which holds a set of
 | 
			
		||||
// surrogate keys on which the data should be sorted.  It uses flags in
 | 
			
		||||
// ConfigState to decide if and how to populate those surrogate keys.
 | 
			
		||||
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
 | 
			
		||||
	vs := &valuesSorter{values: values, cs: cs}
 | 
			
		||||
	if canSortSimply(vs.values[0].Kind()) {
 | 
			
		||||
		return vs
 | 
			
		||||
	}
 | 
			
		||||
	if !cs.DisableMethods {
 | 
			
		||||
		vs.strings = make([]string, len(values))
 | 
			
		||||
		for i := range vs.values {
 | 
			
		||||
			b := bytes.Buffer{}
 | 
			
		||||
			if !handleMethods(cs, &b, vs.values[i]) {
 | 
			
		||||
				vs.strings = nil
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			vs.strings[i] = b.String()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if vs.strings == nil && cs.SpewKeys {
 | 
			
		||||
		vs.strings = make([]string, len(values))
 | 
			
		||||
		for i := range vs.values {
 | 
			
		||||
			vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
 | 
			
		||||
// directly, or whether it should be considered for sorting by surrogate keys
 | 
			
		||||
// (if the ConfigState allows it).
 | 
			
		||||
func canSortSimply(kind reflect.Kind) bool {
 | 
			
		||||
	// This switch parallels valueSortLess, except for the default case.
 | 
			
		||||
	switch kind {
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		return true
 | 
			
		||||
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 | 
			
		||||
		return true
 | 
			
		||||
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
 | 
			
		||||
		return true
 | 
			
		||||
	case reflect.Float32, reflect.Float64:
 | 
			
		||||
		return true
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		return true
 | 
			
		||||
	case reflect.Uintptr:
 | 
			
		||||
		return true
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Len returns the number of values in the slice.  It is part of the
 | 
			
		||||
// sort.Interface implementation.
 | 
			
		||||
func (s *valuesSorter) Len() int {
 | 
			
		||||
	return len(s.values)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Swap swaps the values at the passed indices.  It is part of the
 | 
			
		||||
// sort.Interface implementation.
 | 
			
		||||
func (s *valuesSorter) Swap(i, j int) {
 | 
			
		||||
	s.values[i], s.values[j] = s.values[j], s.values[i]
 | 
			
		||||
	if s.strings != nil {
 | 
			
		||||
		s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// valueSortLess returns whether the first value should sort before the second
 | 
			
		||||
// value.  It is used by valueSorter.Less as part of the sort.Interface
 | 
			
		||||
// implementation.
 | 
			
		||||
func valueSortLess(a, b reflect.Value) bool {
 | 
			
		||||
	switch a.Kind() {
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		return !a.Bool() && b.Bool()
 | 
			
		||||
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 | 
			
		||||
		return a.Int() < b.Int()
 | 
			
		||||
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
 | 
			
		||||
		return a.Uint() < b.Uint()
 | 
			
		||||
	case reflect.Float32, reflect.Float64:
 | 
			
		||||
		return a.Float() < b.Float()
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		return a.String() < b.String()
 | 
			
		||||
	case reflect.Uintptr:
 | 
			
		||||
		return a.Uint() < b.Uint()
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		// Compare the contents of both arrays.
 | 
			
		||||
		l := a.Len()
 | 
			
		||||
		for i := 0; i < l; i++ {
 | 
			
		||||
			av := a.Index(i)
 | 
			
		||||
			bv := b.Index(i)
 | 
			
		||||
			if av.Interface() == bv.Interface() {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			return valueSortLess(av, bv)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return a.String() < b.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Less returns whether the value at index i should sort before the
 | 
			
		||||
// value at index j.  It is part of the sort.Interface implementation.
 | 
			
		||||
func (s *valuesSorter) Less(i, j int) bool {
 | 
			
		||||
	if s.strings == nil {
 | 
			
		||||
		return valueSortLess(s.values[i], s.values[j])
 | 
			
		||||
	}
 | 
			
		||||
	return s.strings[i] < s.strings[j]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sortValues is a sort function that handles both native types and any type that
 | 
			
		||||
// can be converted to error or Stringer.  Other inputs are sorted according to
 | 
			
		||||
// their Value.String() value to ensure display stability.
 | 
			
		||||
func sortValues(values []reflect.Value, cs *ConfigState) {
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(newValuesSorter(values, cs))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										306
									
								
								vendor/github.com/davecgh/go-spew/spew/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										306
									
								
								vendor/github.com/davecgh/go-spew/spew/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,306 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package spew
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ConfigState houses the configuration options used by spew to format and
 | 
			
		||||
// display values.  There is a global instance, Config, that is used to control
 | 
			
		||||
// all top-level Formatter and Dump functionality.  Each ConfigState instance
 | 
			
		||||
// provides methods equivalent to the top-level functions.
 | 
			
		||||
//
 | 
			
		||||
// The zero value for ConfigState provides no indentation.  You would typically
 | 
			
		||||
// want to set it to a space or a tab.
 | 
			
		||||
//
 | 
			
		||||
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
 | 
			
		||||
// with default settings.  See the documentation of NewDefaultConfig for default
 | 
			
		||||
// values.
 | 
			
		||||
type ConfigState struct {
 | 
			
		||||
	// Indent specifies the string to use for each indentation level.  The
 | 
			
		||||
	// global config instance that all top-level functions use set this to a
 | 
			
		||||
	// single space by default.  If you would like more indentation, you might
 | 
			
		||||
	// set this to a tab with "\t" or perhaps two spaces with "  ".
 | 
			
		||||
	Indent string
 | 
			
		||||
 | 
			
		||||
	// MaxDepth controls the maximum number of levels to descend into nested
 | 
			
		||||
	// data structures.  The default, 0, means there is no limit.
 | 
			
		||||
	//
 | 
			
		||||
	// NOTE: Circular data structures are properly detected, so it is not
 | 
			
		||||
	// necessary to set this value unless you specifically want to limit deeply
 | 
			
		||||
	// nested data structures.
 | 
			
		||||
	MaxDepth int
 | 
			
		||||
 | 
			
		||||
	// DisableMethods specifies whether or not error and Stringer interfaces are
 | 
			
		||||
	// invoked for types that implement them.
 | 
			
		||||
	DisableMethods bool
 | 
			
		||||
 | 
			
		||||
	// DisablePointerMethods specifies whether or not to check for and invoke
 | 
			
		||||
	// error and Stringer interfaces on types which only accept a pointer
 | 
			
		||||
	// receiver when the current type is not a pointer.
 | 
			
		||||
	//
 | 
			
		||||
	// NOTE: This might be an unsafe action since calling one of these methods
 | 
			
		||||
	// with a pointer receiver could technically mutate the value, however,
 | 
			
		||||
	// in practice, types which choose to satisify an error or Stringer
 | 
			
		||||
	// interface with a pointer receiver should not be mutating their state
 | 
			
		||||
	// inside these interface methods.  As a result, this option relies on
 | 
			
		||||
	// access to the unsafe package, so it will not have any effect when
 | 
			
		||||
	// running in environments without access to the unsafe package such as
 | 
			
		||||
	// Google App Engine or with the "safe" build tag specified.
 | 
			
		||||
	DisablePointerMethods bool
 | 
			
		||||
 | 
			
		||||
	// DisablePointerAddresses specifies whether to disable the printing of
 | 
			
		||||
	// pointer addresses. This is useful when diffing data structures in tests.
 | 
			
		||||
	DisablePointerAddresses bool
 | 
			
		||||
 | 
			
		||||
	// DisableCapacities specifies whether to disable the printing of capacities
 | 
			
		||||
	// for arrays, slices, maps and channels. This is useful when diffing
 | 
			
		||||
	// data structures in tests.
 | 
			
		||||
	DisableCapacities bool
 | 
			
		||||
 | 
			
		||||
	// ContinueOnMethod specifies whether or not recursion should continue once
 | 
			
		||||
	// a custom error or Stringer interface is invoked.  The default, false,
 | 
			
		||||
	// means it will print the results of invoking the custom error or Stringer
 | 
			
		||||
	// interface and return immediately instead of continuing to recurse into
 | 
			
		||||
	// the internals of the data type.
 | 
			
		||||
	//
 | 
			
		||||
	// NOTE: This flag does not have any effect if method invocation is disabled
 | 
			
		||||
	// via the DisableMethods or DisablePointerMethods options.
 | 
			
		||||
	ContinueOnMethod bool
 | 
			
		||||
 | 
			
		||||
	// SortKeys specifies map keys should be sorted before being printed. Use
 | 
			
		||||
	// this to have a more deterministic, diffable output.  Note that only
 | 
			
		||||
	// native types (bool, int, uint, floats, uintptr and string) and types
 | 
			
		||||
	// that support the error or Stringer interfaces (if methods are
 | 
			
		||||
	// enabled) are supported, with other types sorted according to the
 | 
			
		||||
	// reflect.Value.String() output which guarantees display stability.
 | 
			
		||||
	SortKeys bool
 | 
			
		||||
 | 
			
		||||
	// SpewKeys specifies that, as a last resort attempt, map keys should
 | 
			
		||||
	// be spewed to strings and sorted by those strings.  This is only
 | 
			
		||||
	// considered if SortKeys is true.
 | 
			
		||||
	SpewKeys bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Config is the active configuration of the top-level functions.
 | 
			
		||||
// The configuration can be changed by modifying the contents of spew.Config.
 | 
			
		||||
var Config = ConfigState{Indent: " "}
 | 
			
		||||
 | 
			
		||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the formatted string as a value that satisfies error.  See NewFormatter
 | 
			
		||||
// for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
 | 
			
		||||
	return fmt.Errorf(format, c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Fprint(w, c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintf(w, format, c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintln(w, c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Print(c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Printf(format, c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Println(c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the resulting string.  See NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Sprint(a ...interface{}) string {
 | 
			
		||||
	return fmt.Sprint(c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
 | 
			
		||||
// passed with a Formatter interface returned by c.NewFormatter.  It returns
 | 
			
		||||
// the resulting string.  See NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
 | 
			
		||||
	return fmt.Sprintf(format, c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
 | 
			
		||||
// were passed with a Formatter interface returned by c.NewFormatter.  It
 | 
			
		||||
// returns the resulting string.  See NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
 | 
			
		||||
func (c *ConfigState) Sprintln(a ...interface{}) string {
 | 
			
		||||
	return fmt.Sprintln(c.convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
 | 
			
		||||
interface.  As a result, it integrates cleanly with standard fmt package
 | 
			
		||||
printing functions.  The formatter is useful for inline printing of smaller data
 | 
			
		||||
types similar to the standard %v format specifier.
 | 
			
		||||
 | 
			
		||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
 | 
			
		||||
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
 | 
			
		||||
combinations.  Any other verbs such as %x and %q will be sent to the the
 | 
			
		||||
standard fmt package for formatting.  In addition, the custom formatter ignores
 | 
			
		||||
the width and precision arguments (however they will still work on the format
 | 
			
		||||
specifiers not handled by the custom formatter).
 | 
			
		||||
 | 
			
		||||
Typically this function shouldn't be called directly.  It is much easier to make
 | 
			
		||||
use of the custom formatter by calling one of the convenience functions such as
 | 
			
		||||
c.Printf, c.Println, or c.Printf.
 | 
			
		||||
*/
 | 
			
		||||
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
 | 
			
		||||
	return newFormatter(c, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fdump formats and displays the passed arguments to io.Writer w.  It formats
 | 
			
		||||
// exactly the same as Dump.
 | 
			
		||||
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
 | 
			
		||||
	fdump(c, w, a...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Dump displays the passed parameters to standard out with newlines, customizable
 | 
			
		||||
indentation, and additional debug information such as complete types and all
 | 
			
		||||
pointer addresses used to indirect to the final value.  It provides the
 | 
			
		||||
following features over the built-in printing facilities provided by the fmt
 | 
			
		||||
package:
 | 
			
		||||
 | 
			
		||||
	* Pointers are dereferenced and followed
 | 
			
		||||
	* Circular data structures are detected and handled properly
 | 
			
		||||
	* Custom Stringer/error interfaces are optionally invoked, including
 | 
			
		||||
	  on unexported types
 | 
			
		||||
	* Custom types which only implement the Stringer/error interfaces via
 | 
			
		||||
	  a pointer receiver are optionally invoked when passing non-pointer
 | 
			
		||||
	  variables
 | 
			
		||||
	* Byte arrays and slices are dumped like the hexdump -C command which
 | 
			
		||||
	  includes offsets, byte values in hex, and ASCII output
 | 
			
		||||
 | 
			
		||||
The configuration options are controlled by modifying the public members
 | 
			
		||||
of c.  See ConfigState for options documentation.
 | 
			
		||||
 | 
			
		||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
 | 
			
		||||
get the formatted result as a string.
 | 
			
		||||
*/
 | 
			
		||||
func (c *ConfigState) Dump(a ...interface{}) {
 | 
			
		||||
	fdump(c, os.Stdout, a...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sdump returns a string with the passed arguments formatted exactly the same
 | 
			
		||||
// as Dump.
 | 
			
		||||
func (c *ConfigState) Sdump(a ...interface{}) string {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	fdump(c, &buf, a...)
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertArgs accepts a slice of arguments and returns a slice of the same
 | 
			
		||||
// length with each argument converted to a spew Formatter interface using
 | 
			
		||||
// the ConfigState associated with s.
 | 
			
		||||
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
 | 
			
		||||
	formatters = make([]interface{}, len(args))
 | 
			
		||||
	for index, arg := range args {
 | 
			
		||||
		formatters[index] = newFormatter(c, arg)
 | 
			
		||||
	}
 | 
			
		||||
	return formatters
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDefaultConfig returns a ConfigState with the following default settings.
 | 
			
		||||
//
 | 
			
		||||
// 	Indent: " "
 | 
			
		||||
// 	MaxDepth: 0
 | 
			
		||||
// 	DisableMethods: false
 | 
			
		||||
// 	DisablePointerMethods: false
 | 
			
		||||
// 	ContinueOnMethod: false
 | 
			
		||||
// 	SortKeys: false
 | 
			
		||||
func NewDefaultConfig() *ConfigState {
 | 
			
		||||
	return &ConfigState{Indent: " "}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										211
									
								
								vendor/github.com/davecgh/go-spew/spew/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										211
									
								
								vendor/github.com/davecgh/go-spew/spew/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,211 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Package spew implements a deep pretty printer for Go data structures to aid in
 | 
			
		||||
debugging.
 | 
			
		||||
 | 
			
		||||
A quick overview of the additional features spew provides over the built-in
 | 
			
		||||
printing facilities for Go data types are as follows:
 | 
			
		||||
 | 
			
		||||
	* Pointers are dereferenced and followed
 | 
			
		||||
	* Circular data structures are detected and handled properly
 | 
			
		||||
	* Custom Stringer/error interfaces are optionally invoked, including
 | 
			
		||||
	  on unexported types
 | 
			
		||||
	* Custom types which only implement the Stringer/error interfaces via
 | 
			
		||||
	  a pointer receiver are optionally invoked when passing non-pointer
 | 
			
		||||
	  variables
 | 
			
		||||
	* Byte arrays and slices are dumped like the hexdump -C command which
 | 
			
		||||
	  includes offsets, byte values in hex, and ASCII output (only when using
 | 
			
		||||
	  Dump style)
 | 
			
		||||
 | 
			
		||||
There are two different approaches spew allows for dumping Go data structures:
 | 
			
		||||
 | 
			
		||||
	* Dump style which prints with newlines, customizable indentation,
 | 
			
		||||
	  and additional debug information such as types and all pointer addresses
 | 
			
		||||
	  used to indirect to the final value
 | 
			
		||||
	* A custom Formatter interface that integrates cleanly with the standard fmt
 | 
			
		||||
	  package and replaces %v, %+v, %#v, and %#+v to provide inline printing
 | 
			
		||||
	  similar to the default %v while providing the additional functionality
 | 
			
		||||
	  outlined above and passing unsupported format verbs such as %x and %q
 | 
			
		||||
	  along to fmt
 | 
			
		||||
 | 
			
		||||
Quick Start
 | 
			
		||||
 | 
			
		||||
This section demonstrates how to quickly get started with spew.  See the
 | 
			
		||||
sections below for further details on formatting and configuration options.
 | 
			
		||||
 | 
			
		||||
To dump a variable with full newlines, indentation, type, and pointer
 | 
			
		||||
information use Dump, Fdump, or Sdump:
 | 
			
		||||
	spew.Dump(myVar1, myVar2, ...)
 | 
			
		||||
	spew.Fdump(someWriter, myVar1, myVar2, ...)
 | 
			
		||||
	str := spew.Sdump(myVar1, myVar2, ...)
 | 
			
		||||
 | 
			
		||||
Alternatively, if you would prefer to use format strings with a compacted inline
 | 
			
		||||
printing style, use the convenience wrappers Printf, Fprintf, etc with
 | 
			
		||||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
 | 
			
		||||
%#+v (adds types and pointer addresses):
 | 
			
		||||
	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
 | 
			
		||||
	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
 | 
			
		||||
	spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
 | 
			
		||||
	spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
 | 
			
		||||
 | 
			
		||||
Configuration Options
 | 
			
		||||
 | 
			
		||||
Configuration of spew is handled by fields in the ConfigState type.  For
 | 
			
		||||
convenience, all of the top-level functions use a global state available
 | 
			
		||||
via the spew.Config global.
 | 
			
		||||
 | 
			
		||||
It is also possible to create a ConfigState instance that provides methods
 | 
			
		||||
equivalent to the top-level functions.  This allows concurrent configuration
 | 
			
		||||
options.  See the ConfigState documentation for more details.
 | 
			
		||||
 | 
			
		||||
The following configuration options are available:
 | 
			
		||||
	* Indent
 | 
			
		||||
		String to use for each indentation level for Dump functions.
 | 
			
		||||
		It is a single space by default.  A popular alternative is "\t".
 | 
			
		||||
 | 
			
		||||
	* MaxDepth
 | 
			
		||||
		Maximum number of levels to descend into nested data structures.
 | 
			
		||||
		There is no limit by default.
 | 
			
		||||
 | 
			
		||||
	* DisableMethods
 | 
			
		||||
		Disables invocation of error and Stringer interface methods.
 | 
			
		||||
		Method invocation is enabled by default.
 | 
			
		||||
 | 
			
		||||
	* DisablePointerMethods
 | 
			
		||||
		Disables invocation of error and Stringer interface methods on types
 | 
			
		||||
		which only accept pointer receivers from non-pointer variables.
 | 
			
		||||
		Pointer method invocation is enabled by default.
 | 
			
		||||
 | 
			
		||||
	* DisablePointerAddresses
 | 
			
		||||
		DisablePointerAddresses specifies whether to disable the printing of
 | 
			
		||||
		pointer addresses. This is useful when diffing data structures in tests.
 | 
			
		||||
 | 
			
		||||
	* DisableCapacities
 | 
			
		||||
		DisableCapacities specifies whether to disable the printing of
 | 
			
		||||
		capacities for arrays, slices, maps and channels. This is useful when
 | 
			
		||||
		diffing data structures in tests.
 | 
			
		||||
 | 
			
		||||
	* ContinueOnMethod
 | 
			
		||||
		Enables recursion into types after invoking error and Stringer interface
 | 
			
		||||
		methods. Recursion after method invocation is disabled by default.
 | 
			
		||||
 | 
			
		||||
	* SortKeys
 | 
			
		||||
		Specifies map keys should be sorted before being printed. Use
 | 
			
		||||
		this to have a more deterministic, diffable output.  Note that
 | 
			
		||||
		only native types (bool, int, uint, floats, uintptr and string)
 | 
			
		||||
		and types which implement error or Stringer interfaces are
 | 
			
		||||
		supported with other types sorted according to the
 | 
			
		||||
		reflect.Value.String() output which guarantees display
 | 
			
		||||
		stability.  Natural map order is used by default.
 | 
			
		||||
 | 
			
		||||
	* SpewKeys
 | 
			
		||||
		Specifies that, as a last resort attempt, map keys should be
 | 
			
		||||
		spewed to strings and sorted by those strings.  This is only
 | 
			
		||||
		considered if SortKeys is true.
 | 
			
		||||
 | 
			
		||||
Dump Usage
 | 
			
		||||
 | 
			
		||||
Simply call spew.Dump with a list of variables you want to dump:
 | 
			
		||||
 | 
			
		||||
	spew.Dump(myVar1, myVar2, ...)
 | 
			
		||||
 | 
			
		||||
You may also call spew.Fdump if you would prefer to output to an arbitrary
 | 
			
		||||
io.Writer.  For example, to dump to standard error:
 | 
			
		||||
 | 
			
		||||
	spew.Fdump(os.Stderr, myVar1, myVar2, ...)
 | 
			
		||||
 | 
			
		||||
A third option is to call spew.Sdump to get the formatted output as a string:
 | 
			
		||||
 | 
			
		||||
	str := spew.Sdump(myVar1, myVar2, ...)
 | 
			
		||||
 | 
			
		||||
Sample Dump Output
 | 
			
		||||
 | 
			
		||||
See the Dump example for details on the setup of the types and variables being
 | 
			
		||||
shown here.
 | 
			
		||||
 | 
			
		||||
	(main.Foo) {
 | 
			
		||||
	 unexportedField: (*main.Bar)(0xf84002e210)({
 | 
			
		||||
	  flag: (main.Flag) flagTwo,
 | 
			
		||||
	  data: (uintptr) <nil>
 | 
			
		||||
	 }),
 | 
			
		||||
	 ExportedField: (map[interface {}]interface {}) (len=1) {
 | 
			
		||||
	  (string) (len=3) "one": (bool) true
 | 
			
		||||
	 }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
 | 
			
		||||
command as shown.
 | 
			
		||||
	([]uint8) (len=32 cap=32) {
 | 
			
		||||
	 00000000  11 12 13 14 15 16 17 18  19 1a 1b 1c 1d 1e 1f 20  |............... |
 | 
			
		||||
	 00000010  21 22 23 24 25 26 27 28  29 2a 2b 2c 2d 2e 2f 30  |!"#$%&'()*+,-./0|
 | 
			
		||||
	 00000020  31 32                                             |12|
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Custom Formatter
 | 
			
		||||
 | 
			
		||||
Spew provides a custom formatter that implements the fmt.Formatter interface
 | 
			
		||||
so that it integrates cleanly with standard fmt package printing functions. The
 | 
			
		||||
formatter is useful for inline printing of smaller data types similar to the
 | 
			
		||||
standard %v format specifier.
 | 
			
		||||
 | 
			
		||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
 | 
			
		||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
 | 
			
		||||
combinations.  Any other verbs such as %x and %q will be sent to the the
 | 
			
		||||
standard fmt package for formatting.  In addition, the custom formatter ignores
 | 
			
		||||
the width and precision arguments (however they will still work on the format
 | 
			
		||||
specifiers not handled by the custom formatter).
 | 
			
		||||
 | 
			
		||||
Custom Formatter Usage
 | 
			
		||||
 | 
			
		||||
The simplest way to make use of the spew custom formatter is to call one of the
 | 
			
		||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf.  The
 | 
			
		||||
functions have syntax you are most likely already familiar with:
 | 
			
		||||
 | 
			
		||||
	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
 | 
			
		||||
	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
 | 
			
		||||
	spew.Println(myVar, myVar2)
 | 
			
		||||
	spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
 | 
			
		||||
	spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
 | 
			
		||||
 | 
			
		||||
See the Index for the full list convenience functions.
 | 
			
		||||
 | 
			
		||||
Sample Formatter Output
 | 
			
		||||
 | 
			
		||||
Double pointer to a uint8:
 | 
			
		||||
	  %v: <**>5
 | 
			
		||||
	 %+v: <**>(0xf8400420d0->0xf8400420c8)5
 | 
			
		||||
	 %#v: (**uint8)5
 | 
			
		||||
	%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
 | 
			
		||||
 | 
			
		||||
Pointer to circular struct with a uint8 field and a pointer to itself:
 | 
			
		||||
	  %v: <*>{1 <*><shown>}
 | 
			
		||||
	 %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
 | 
			
		||||
	 %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
 | 
			
		||||
	%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
 | 
			
		||||
 | 
			
		||||
See the Printf example for details on the setup of variables being shown
 | 
			
		||||
here.
 | 
			
		||||
 | 
			
		||||
Errors
 | 
			
		||||
 | 
			
		||||
Since it is possible for custom Stringer/error interfaces to panic, spew
 | 
			
		||||
detects them and handles them internally by printing the panic information
 | 
			
		||||
inline with the output.  Since spew is intended to provide deep pretty printing
 | 
			
		||||
capabilities on structures, it intentionally does not return any errors.
 | 
			
		||||
*/
 | 
			
		||||
package spew
 | 
			
		||||
							
								
								
									
										509
									
								
								vendor/github.com/davecgh/go-spew/spew/dump.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										509
									
								
								vendor/github.com/davecgh/go-spew/spew/dump.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,509 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package spew
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// uint8Type is a reflect.Type representing a uint8.  It is used to
 | 
			
		||||
	// convert cgo types to uint8 slices for hexdumping.
 | 
			
		||||
	uint8Type = reflect.TypeOf(uint8(0))
 | 
			
		||||
 | 
			
		||||
	// cCharRE is a regular expression that matches a cgo char.
 | 
			
		||||
	// It is used to detect character arrays to hexdump them.
 | 
			
		||||
	cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
 | 
			
		||||
 | 
			
		||||
	// cUnsignedCharRE is a regular expression that matches a cgo unsigned
 | 
			
		||||
	// char.  It is used to detect unsigned character arrays to hexdump
 | 
			
		||||
	// them.
 | 
			
		||||
	cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
 | 
			
		||||
 | 
			
		||||
	// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
 | 
			
		||||
	// It is used to detect uint8_t arrays to hexdump them.
 | 
			
		||||
	cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// dumpState contains information about the state of a dump operation.
 | 
			
		||||
type dumpState struct {
 | 
			
		||||
	w                io.Writer
 | 
			
		||||
	depth            int
 | 
			
		||||
	pointers         map[uintptr]int
 | 
			
		||||
	ignoreNextType   bool
 | 
			
		||||
	ignoreNextIndent bool
 | 
			
		||||
	cs               *ConfigState
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// indent performs indentation according to the depth level and cs.Indent
 | 
			
		||||
// option.
 | 
			
		||||
func (d *dumpState) indent() {
 | 
			
		||||
	if d.ignoreNextIndent {
 | 
			
		||||
		d.ignoreNextIndent = false
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unpackValue returns values inside of non-nil interfaces when possible.
 | 
			
		||||
// This is useful for data types like structs, arrays, slices, and maps which
 | 
			
		||||
// can contain varying types packed inside an interface.
 | 
			
		||||
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
 | 
			
		||||
	if v.Kind() == reflect.Interface && !v.IsNil() {
 | 
			
		||||
		v = v.Elem()
 | 
			
		||||
	}
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
 | 
			
		||||
func (d *dumpState) dumpPtr(v reflect.Value) {
 | 
			
		||||
	// Remove pointers at or below the current depth from map used to detect
 | 
			
		||||
	// circular refs.
 | 
			
		||||
	for k, depth := range d.pointers {
 | 
			
		||||
		if depth >= d.depth {
 | 
			
		||||
			delete(d.pointers, k)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Keep list of all dereferenced pointers to show later.
 | 
			
		||||
	pointerChain := make([]uintptr, 0)
 | 
			
		||||
 | 
			
		||||
	// Figure out how many levels of indirection there are by dereferencing
 | 
			
		||||
	// pointers and unpacking interfaces down the chain while detecting circular
 | 
			
		||||
	// references.
 | 
			
		||||
	nilFound := false
 | 
			
		||||
	cycleFound := false
 | 
			
		||||
	indirects := 0
 | 
			
		||||
	ve := v
 | 
			
		||||
	for ve.Kind() == reflect.Ptr {
 | 
			
		||||
		if ve.IsNil() {
 | 
			
		||||
			nilFound = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		indirects++
 | 
			
		||||
		addr := ve.Pointer()
 | 
			
		||||
		pointerChain = append(pointerChain, addr)
 | 
			
		||||
		if pd, ok := d.pointers[addr]; ok && pd < d.depth {
 | 
			
		||||
			cycleFound = true
 | 
			
		||||
			indirects--
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		d.pointers[addr] = d.depth
 | 
			
		||||
 | 
			
		||||
		ve = ve.Elem()
 | 
			
		||||
		if ve.Kind() == reflect.Interface {
 | 
			
		||||
			if ve.IsNil() {
 | 
			
		||||
				nilFound = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			ve = ve.Elem()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Display type information.
 | 
			
		||||
	d.w.Write(openParenBytes)
 | 
			
		||||
	d.w.Write(bytes.Repeat(asteriskBytes, indirects))
 | 
			
		||||
	d.w.Write([]byte(ve.Type().String()))
 | 
			
		||||
	d.w.Write(closeParenBytes)
 | 
			
		||||
 | 
			
		||||
	// Display pointer information.
 | 
			
		||||
	if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
 | 
			
		||||
		d.w.Write(openParenBytes)
 | 
			
		||||
		for i, addr := range pointerChain {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				d.w.Write(pointerChainBytes)
 | 
			
		||||
			}
 | 
			
		||||
			printHexPtr(d.w, addr)
 | 
			
		||||
		}
 | 
			
		||||
		d.w.Write(closeParenBytes)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Display dereferenced value.
 | 
			
		||||
	d.w.Write(openParenBytes)
 | 
			
		||||
	switch {
 | 
			
		||||
	case nilFound == true:
 | 
			
		||||
		d.w.Write(nilAngleBytes)
 | 
			
		||||
 | 
			
		||||
	case cycleFound == true:
 | 
			
		||||
		d.w.Write(circularBytes)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		d.ignoreNextType = true
 | 
			
		||||
		d.dump(ve)
 | 
			
		||||
	}
 | 
			
		||||
	d.w.Write(closeParenBytes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dumpSlice handles formatting of arrays and slices.  Byte (uint8 under
 | 
			
		||||
// reflection) arrays and slices are dumped in hexdump -C fashion.
 | 
			
		||||
func (d *dumpState) dumpSlice(v reflect.Value) {
 | 
			
		||||
	// Determine whether this type should be hex dumped or not.  Also,
 | 
			
		||||
	// for types which should be hexdumped, try to use the underlying data
 | 
			
		||||
	// first, then fall back to trying to convert them to a uint8 slice.
 | 
			
		||||
	var buf []uint8
 | 
			
		||||
	doConvert := false
 | 
			
		||||
	doHexDump := false
 | 
			
		||||
	numEntries := v.Len()
 | 
			
		||||
	if numEntries > 0 {
 | 
			
		||||
		vt := v.Index(0).Type()
 | 
			
		||||
		vts := vt.String()
 | 
			
		||||
		switch {
 | 
			
		||||
		// C types that need to be converted.
 | 
			
		||||
		case cCharRE.MatchString(vts):
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case cUnsignedCharRE.MatchString(vts):
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case cUint8tCharRE.MatchString(vts):
 | 
			
		||||
			doConvert = true
 | 
			
		||||
 | 
			
		||||
		// Try to use existing uint8 slices and fall back to converting
 | 
			
		||||
		// and copying if that fails.
 | 
			
		||||
		case vt.Kind() == reflect.Uint8:
 | 
			
		||||
			// We need an addressable interface to convert the type
 | 
			
		||||
			// to a byte slice.  However, the reflect package won't
 | 
			
		||||
			// give us an interface on certain things like
 | 
			
		||||
			// unexported struct fields in order to enforce
 | 
			
		||||
			// visibility rules.  We use unsafe, when available, to
 | 
			
		||||
			// bypass these restrictions since this package does not
 | 
			
		||||
			// mutate the values.
 | 
			
		||||
			vs := v
 | 
			
		||||
			if !vs.CanInterface() || !vs.CanAddr() {
 | 
			
		||||
				vs = unsafeReflectValue(vs)
 | 
			
		||||
			}
 | 
			
		||||
			if !UnsafeDisabled {
 | 
			
		||||
				vs = vs.Slice(0, numEntries)
 | 
			
		||||
 | 
			
		||||
				// Use the existing uint8 slice if it can be
 | 
			
		||||
				// type asserted.
 | 
			
		||||
				iface := vs.Interface()
 | 
			
		||||
				if slice, ok := iface.([]uint8); ok {
 | 
			
		||||
					buf = slice
 | 
			
		||||
					doHexDump = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// The underlying data needs to be converted if it can't
 | 
			
		||||
			// be type asserted to a uint8 slice.
 | 
			
		||||
			doConvert = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Copy and convert the underlying type if needed.
 | 
			
		||||
		if doConvert && vt.ConvertibleTo(uint8Type) {
 | 
			
		||||
			// Convert and copy each element into a uint8 byte
 | 
			
		||||
			// slice.
 | 
			
		||||
			buf = make([]uint8, numEntries)
 | 
			
		||||
			for i := 0; i < numEntries; i++ {
 | 
			
		||||
				vv := v.Index(i)
 | 
			
		||||
				buf[i] = uint8(vv.Convert(uint8Type).Uint())
 | 
			
		||||
			}
 | 
			
		||||
			doHexDump = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hexdump the entire slice as needed.
 | 
			
		||||
	if doHexDump {
 | 
			
		||||
		indent := strings.Repeat(d.cs.Indent, d.depth)
 | 
			
		||||
		str := indent + hex.Dump(buf)
 | 
			
		||||
		str = strings.Replace(str, "\n", "\n"+indent, -1)
 | 
			
		||||
		str = strings.TrimRight(str, d.cs.Indent)
 | 
			
		||||
		d.w.Write([]byte(str))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Recursively call dump for each item.
 | 
			
		||||
	for i := 0; i < numEntries; i++ {
 | 
			
		||||
		d.dump(d.unpackValue(v.Index(i)))
 | 
			
		||||
		if i < (numEntries - 1) {
 | 
			
		||||
			d.w.Write(commaNewlineBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			d.w.Write(newlineBytes)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dump is the main workhorse for dumping a value.  It uses the passed reflect
 | 
			
		||||
// value to figure out what kind of object we are dealing with and formats it
 | 
			
		||||
// appropriately.  It is a recursive function, however circular data structures
 | 
			
		||||
// are detected and handled properly.
 | 
			
		||||
func (d *dumpState) dump(v reflect.Value) {
 | 
			
		||||
	// Handle invalid reflect values immediately.
 | 
			
		||||
	kind := v.Kind()
 | 
			
		||||
	if kind == reflect.Invalid {
 | 
			
		||||
		d.w.Write(invalidAngleBytes)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle pointers specially.
 | 
			
		||||
	if kind == reflect.Ptr {
 | 
			
		||||
		d.indent()
 | 
			
		||||
		d.dumpPtr(v)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Print type information unless already handled elsewhere.
 | 
			
		||||
	if !d.ignoreNextType {
 | 
			
		||||
		d.indent()
 | 
			
		||||
		d.w.Write(openParenBytes)
 | 
			
		||||
		d.w.Write([]byte(v.Type().String()))
 | 
			
		||||
		d.w.Write(closeParenBytes)
 | 
			
		||||
		d.w.Write(spaceBytes)
 | 
			
		||||
	}
 | 
			
		||||
	d.ignoreNextType = false
 | 
			
		||||
 | 
			
		||||
	// Display length and capacity if the built-in len and cap functions
 | 
			
		||||
	// work with the value's kind and the len/cap itself is non-zero.
 | 
			
		||||
	valueLen, valueCap := 0, 0
 | 
			
		||||
	switch v.Kind() {
 | 
			
		||||
	case reflect.Array, reflect.Slice, reflect.Chan:
 | 
			
		||||
		valueLen, valueCap = v.Len(), v.Cap()
 | 
			
		||||
	case reflect.Map, reflect.String:
 | 
			
		||||
		valueLen = v.Len()
 | 
			
		||||
	}
 | 
			
		||||
	if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
 | 
			
		||||
		d.w.Write(openParenBytes)
 | 
			
		||||
		if valueLen != 0 {
 | 
			
		||||
			d.w.Write(lenEqualsBytes)
 | 
			
		||||
			printInt(d.w, int64(valueLen), 10)
 | 
			
		||||
		}
 | 
			
		||||
		if !d.cs.DisableCapacities && valueCap != 0 {
 | 
			
		||||
			if valueLen != 0 {
 | 
			
		||||
				d.w.Write(spaceBytes)
 | 
			
		||||
			}
 | 
			
		||||
			d.w.Write(capEqualsBytes)
 | 
			
		||||
			printInt(d.w, int64(valueCap), 10)
 | 
			
		||||
		}
 | 
			
		||||
		d.w.Write(closeParenBytes)
 | 
			
		||||
		d.w.Write(spaceBytes)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Call Stringer/error interfaces if they exist and the handle methods flag
 | 
			
		||||
	// is enabled
 | 
			
		||||
	if !d.cs.DisableMethods {
 | 
			
		||||
		if (kind != reflect.Invalid) && (kind != reflect.Interface) {
 | 
			
		||||
			if handled := handleMethods(d.cs, d.w, v); handled {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch kind {
 | 
			
		||||
	case reflect.Invalid:
 | 
			
		||||
		// Do nothing.  We should never get here since invalid has already
 | 
			
		||||
		// been handled above.
 | 
			
		||||
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		printBool(d.w, v.Bool())
 | 
			
		||||
 | 
			
		||||
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 | 
			
		||||
		printInt(d.w, v.Int(), 10)
 | 
			
		||||
 | 
			
		||||
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
 | 
			
		||||
		printUint(d.w, v.Uint(), 10)
 | 
			
		||||
 | 
			
		||||
	case reflect.Float32:
 | 
			
		||||
		printFloat(d.w, v.Float(), 32)
 | 
			
		||||
 | 
			
		||||
	case reflect.Float64:
 | 
			
		||||
		printFloat(d.w, v.Float(), 64)
 | 
			
		||||
 | 
			
		||||
	case reflect.Complex64:
 | 
			
		||||
		printComplex(d.w, v.Complex(), 32)
 | 
			
		||||
 | 
			
		||||
	case reflect.Complex128:
 | 
			
		||||
		printComplex(d.w, v.Complex(), 64)
 | 
			
		||||
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			d.w.Write(nilAngleBytes)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		fallthrough
 | 
			
		||||
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		d.w.Write(openBraceNewlineBytes)
 | 
			
		||||
		d.depth++
 | 
			
		||||
		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
 | 
			
		||||
			d.indent()
 | 
			
		||||
			d.w.Write(maxNewlineBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			d.dumpSlice(v)
 | 
			
		||||
		}
 | 
			
		||||
		d.depth--
 | 
			
		||||
		d.indent()
 | 
			
		||||
		d.w.Write(closeBraceBytes)
 | 
			
		||||
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		d.w.Write([]byte(strconv.Quote(v.String())))
 | 
			
		||||
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		// The only time we should get here is for nil interfaces due to
 | 
			
		||||
		// unpackValue calls.
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			d.w.Write(nilAngleBytes)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		// Do nothing.  We should never get here since pointers have already
 | 
			
		||||
		// been handled above.
 | 
			
		||||
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		// nil maps should be indicated as different than empty maps
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			d.w.Write(nilAngleBytes)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		d.w.Write(openBraceNewlineBytes)
 | 
			
		||||
		d.depth++
 | 
			
		||||
		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
 | 
			
		||||
			d.indent()
 | 
			
		||||
			d.w.Write(maxNewlineBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			numEntries := v.Len()
 | 
			
		||||
			keys := v.MapKeys()
 | 
			
		||||
			if d.cs.SortKeys {
 | 
			
		||||
				sortValues(keys, d.cs)
 | 
			
		||||
			}
 | 
			
		||||
			for i, key := range keys {
 | 
			
		||||
				d.dump(d.unpackValue(key))
 | 
			
		||||
				d.w.Write(colonSpaceBytes)
 | 
			
		||||
				d.ignoreNextIndent = true
 | 
			
		||||
				d.dump(d.unpackValue(v.MapIndex(key)))
 | 
			
		||||
				if i < (numEntries - 1) {
 | 
			
		||||
					d.w.Write(commaNewlineBytes)
 | 
			
		||||
				} else {
 | 
			
		||||
					d.w.Write(newlineBytes)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		d.depth--
 | 
			
		||||
		d.indent()
 | 
			
		||||
		d.w.Write(closeBraceBytes)
 | 
			
		||||
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		d.w.Write(openBraceNewlineBytes)
 | 
			
		||||
		d.depth++
 | 
			
		||||
		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
 | 
			
		||||
			d.indent()
 | 
			
		||||
			d.w.Write(maxNewlineBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			vt := v.Type()
 | 
			
		||||
			numFields := v.NumField()
 | 
			
		||||
			for i := 0; i < numFields; i++ {
 | 
			
		||||
				d.indent()
 | 
			
		||||
				vtf := vt.Field(i)
 | 
			
		||||
				d.w.Write([]byte(vtf.Name))
 | 
			
		||||
				d.w.Write(colonSpaceBytes)
 | 
			
		||||
				d.ignoreNextIndent = true
 | 
			
		||||
				d.dump(d.unpackValue(v.Field(i)))
 | 
			
		||||
				if i < (numFields - 1) {
 | 
			
		||||
					d.w.Write(commaNewlineBytes)
 | 
			
		||||
				} else {
 | 
			
		||||
					d.w.Write(newlineBytes)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		d.depth--
 | 
			
		||||
		d.indent()
 | 
			
		||||
		d.w.Write(closeBraceBytes)
 | 
			
		||||
 | 
			
		||||
	case reflect.Uintptr:
 | 
			
		||||
		printHexPtr(d.w, uintptr(v.Uint()))
 | 
			
		||||
 | 
			
		||||
	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
 | 
			
		||||
		printHexPtr(d.w, v.Pointer())
 | 
			
		||||
 | 
			
		||||
	// There were not any other types at the time this code was written, but
 | 
			
		||||
	// fall back to letting the default fmt package handle it in case any new
 | 
			
		||||
	// types are added.
 | 
			
		||||
	default:
 | 
			
		||||
		if v.CanInterface() {
 | 
			
		||||
			fmt.Fprintf(d.w, "%v", v.Interface())
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(d.w, "%v", v.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fdump is a helper function to consolidate the logic from the various public
 | 
			
		||||
// methods which take varying writers and config states.
 | 
			
		||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
 | 
			
		||||
	for _, arg := range a {
 | 
			
		||||
		if arg == nil {
 | 
			
		||||
			w.Write(interfaceBytes)
 | 
			
		||||
			w.Write(spaceBytes)
 | 
			
		||||
			w.Write(nilAngleBytes)
 | 
			
		||||
			w.Write(newlineBytes)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		d := dumpState{w: w, cs: cs}
 | 
			
		||||
		d.pointers = make(map[uintptr]int)
 | 
			
		||||
		d.dump(reflect.ValueOf(arg))
 | 
			
		||||
		d.w.Write(newlineBytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fdump formats and displays the passed arguments to io.Writer w.  It formats
 | 
			
		||||
// exactly the same as Dump.
 | 
			
		||||
func Fdump(w io.Writer, a ...interface{}) {
 | 
			
		||||
	fdump(&Config, w, a...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sdump returns a string with the passed arguments formatted exactly the same
 | 
			
		||||
// as Dump.
 | 
			
		||||
func Sdump(a ...interface{}) string {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	fdump(&Config, &buf, a...)
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Dump displays the passed parameters to standard out with newlines, customizable
 | 
			
		||||
indentation, and additional debug information such as complete types and all
 | 
			
		||||
pointer addresses used to indirect to the final value.  It provides the
 | 
			
		||||
following features over the built-in printing facilities provided by the fmt
 | 
			
		||||
package:
 | 
			
		||||
 | 
			
		||||
	* Pointers are dereferenced and followed
 | 
			
		||||
	* Circular data structures are detected and handled properly
 | 
			
		||||
	* Custom Stringer/error interfaces are optionally invoked, including
 | 
			
		||||
	  on unexported types
 | 
			
		||||
	* Custom types which only implement the Stringer/error interfaces via
 | 
			
		||||
	  a pointer receiver are optionally invoked when passing non-pointer
 | 
			
		||||
	  variables
 | 
			
		||||
	* Byte arrays and slices are dumped like the hexdump -C command which
 | 
			
		||||
	  includes offsets, byte values in hex, and ASCII output
 | 
			
		||||
 | 
			
		||||
The configuration options are controlled by an exported package global,
 | 
			
		||||
spew.Config.  See ConfigState for options documentation.
 | 
			
		||||
 | 
			
		||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
 | 
			
		||||
get the formatted result as a string.
 | 
			
		||||
*/
 | 
			
		||||
func Dump(a ...interface{}) {
 | 
			
		||||
	fdump(&Config, os.Stdout, a...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										419
									
								
								vendor/github.com/davecgh/go-spew/spew/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										419
									
								
								vendor/github.com/davecgh/go-spew/spew/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,419 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package spew
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// supportedFlags is a list of all the character flags supported by fmt package.
 | 
			
		||||
const supportedFlags = "0-+# "
 | 
			
		||||
 | 
			
		||||
// formatState implements the fmt.Formatter interface and contains information
 | 
			
		||||
// about the state of a formatting operation.  The NewFormatter function can
 | 
			
		||||
// be used to get a new Formatter which can be used directly as arguments
 | 
			
		||||
// in standard fmt package printing calls.
 | 
			
		||||
type formatState struct {
 | 
			
		||||
	value          interface{}
 | 
			
		||||
	fs             fmt.State
 | 
			
		||||
	depth          int
 | 
			
		||||
	pointers       map[uintptr]int
 | 
			
		||||
	ignoreNextType bool
 | 
			
		||||
	cs             *ConfigState
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// buildDefaultFormat recreates the original format string without precision
 | 
			
		||||
// and width information to pass in to fmt.Sprintf in the case of an
 | 
			
		||||
// unrecognized type.  Unless new types are added to the language, this
 | 
			
		||||
// function won't ever be called.
 | 
			
		||||
func (f *formatState) buildDefaultFormat() (format string) {
 | 
			
		||||
	buf := bytes.NewBuffer(percentBytes)
 | 
			
		||||
 | 
			
		||||
	for _, flag := range supportedFlags {
 | 
			
		||||
		if f.fs.Flag(int(flag)) {
 | 
			
		||||
			buf.WriteRune(flag)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf.WriteRune('v')
 | 
			
		||||
 | 
			
		||||
	format = buf.String()
 | 
			
		||||
	return format
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// constructOrigFormat recreates the original format string including precision
 | 
			
		||||
// and width information to pass along to the standard fmt package.  This allows
 | 
			
		||||
// automatic deferral of all format strings this package doesn't support.
 | 
			
		||||
func (f *formatState) constructOrigFormat(verb rune) (format string) {
 | 
			
		||||
	buf := bytes.NewBuffer(percentBytes)
 | 
			
		||||
 | 
			
		||||
	for _, flag := range supportedFlags {
 | 
			
		||||
		if f.fs.Flag(int(flag)) {
 | 
			
		||||
			buf.WriteRune(flag)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if width, ok := f.fs.Width(); ok {
 | 
			
		||||
		buf.WriteString(strconv.Itoa(width))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if precision, ok := f.fs.Precision(); ok {
 | 
			
		||||
		buf.Write(precisionBytes)
 | 
			
		||||
		buf.WriteString(strconv.Itoa(precision))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf.WriteRune(verb)
 | 
			
		||||
 | 
			
		||||
	format = buf.String()
 | 
			
		||||
	return format
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unpackValue returns values inside of non-nil interfaces when possible and
 | 
			
		||||
// ensures that types for values which have been unpacked from an interface
 | 
			
		||||
// are displayed when the show types flag is also set.
 | 
			
		||||
// This is useful for data types like structs, arrays, slices, and maps which
 | 
			
		||||
// can contain varying types packed inside an interface.
 | 
			
		||||
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
 | 
			
		||||
	if v.Kind() == reflect.Interface {
 | 
			
		||||
		f.ignoreNextType = false
 | 
			
		||||
		if !v.IsNil() {
 | 
			
		||||
			v = v.Elem()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
 | 
			
		||||
func (f *formatState) formatPtr(v reflect.Value) {
 | 
			
		||||
	// Display nil if top level pointer is nil.
 | 
			
		||||
	showTypes := f.fs.Flag('#')
 | 
			
		||||
	if v.IsNil() && (!showTypes || f.ignoreNextType) {
 | 
			
		||||
		f.fs.Write(nilAngleBytes)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove pointers at or below the current depth from map used to detect
 | 
			
		||||
	// circular refs.
 | 
			
		||||
	for k, depth := range f.pointers {
 | 
			
		||||
		if depth >= f.depth {
 | 
			
		||||
			delete(f.pointers, k)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Keep list of all dereferenced pointers to possibly show later.
 | 
			
		||||
	pointerChain := make([]uintptr, 0)
 | 
			
		||||
 | 
			
		||||
	// Figure out how many levels of indirection there are by derferencing
 | 
			
		||||
	// pointers and unpacking interfaces down the chain while detecting circular
 | 
			
		||||
	// references.
 | 
			
		||||
	nilFound := false
 | 
			
		||||
	cycleFound := false
 | 
			
		||||
	indirects := 0
 | 
			
		||||
	ve := v
 | 
			
		||||
	for ve.Kind() == reflect.Ptr {
 | 
			
		||||
		if ve.IsNil() {
 | 
			
		||||
			nilFound = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		indirects++
 | 
			
		||||
		addr := ve.Pointer()
 | 
			
		||||
		pointerChain = append(pointerChain, addr)
 | 
			
		||||
		if pd, ok := f.pointers[addr]; ok && pd < f.depth {
 | 
			
		||||
			cycleFound = true
 | 
			
		||||
			indirects--
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		f.pointers[addr] = f.depth
 | 
			
		||||
 | 
			
		||||
		ve = ve.Elem()
 | 
			
		||||
		if ve.Kind() == reflect.Interface {
 | 
			
		||||
			if ve.IsNil() {
 | 
			
		||||
				nilFound = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			ve = ve.Elem()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Display type or indirection level depending on flags.
 | 
			
		||||
	if showTypes && !f.ignoreNextType {
 | 
			
		||||
		f.fs.Write(openParenBytes)
 | 
			
		||||
		f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
 | 
			
		||||
		f.fs.Write([]byte(ve.Type().String()))
 | 
			
		||||
		f.fs.Write(closeParenBytes)
 | 
			
		||||
	} else {
 | 
			
		||||
		if nilFound || cycleFound {
 | 
			
		||||
			indirects += strings.Count(ve.Type().String(), "*")
 | 
			
		||||
		}
 | 
			
		||||
		f.fs.Write(openAngleBytes)
 | 
			
		||||
		f.fs.Write([]byte(strings.Repeat("*", indirects)))
 | 
			
		||||
		f.fs.Write(closeAngleBytes)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Display pointer information depending on flags.
 | 
			
		||||
	if f.fs.Flag('+') && (len(pointerChain) > 0) {
 | 
			
		||||
		f.fs.Write(openParenBytes)
 | 
			
		||||
		for i, addr := range pointerChain {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				f.fs.Write(pointerChainBytes)
 | 
			
		||||
			}
 | 
			
		||||
			printHexPtr(f.fs, addr)
 | 
			
		||||
		}
 | 
			
		||||
		f.fs.Write(closeParenBytes)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Display dereferenced value.
 | 
			
		||||
	switch {
 | 
			
		||||
	case nilFound == true:
 | 
			
		||||
		f.fs.Write(nilAngleBytes)
 | 
			
		||||
 | 
			
		||||
	case cycleFound == true:
 | 
			
		||||
		f.fs.Write(circularShortBytes)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		f.ignoreNextType = true
 | 
			
		||||
		f.format(ve)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// format is the main workhorse for providing the Formatter interface.  It
 | 
			
		||||
// uses the passed reflect value to figure out what kind of object we are
 | 
			
		||||
// dealing with and formats it appropriately.  It is a recursive function,
 | 
			
		||||
// however circular data structures are detected and handled properly.
 | 
			
		||||
func (f *formatState) format(v reflect.Value) {
 | 
			
		||||
	// Handle invalid reflect values immediately.
 | 
			
		||||
	kind := v.Kind()
 | 
			
		||||
	if kind == reflect.Invalid {
 | 
			
		||||
		f.fs.Write(invalidAngleBytes)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle pointers specially.
 | 
			
		||||
	if kind == reflect.Ptr {
 | 
			
		||||
		f.formatPtr(v)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Print type information unless already handled elsewhere.
 | 
			
		||||
	if !f.ignoreNextType && f.fs.Flag('#') {
 | 
			
		||||
		f.fs.Write(openParenBytes)
 | 
			
		||||
		f.fs.Write([]byte(v.Type().String()))
 | 
			
		||||
		f.fs.Write(closeParenBytes)
 | 
			
		||||
	}
 | 
			
		||||
	f.ignoreNextType = false
 | 
			
		||||
 | 
			
		||||
	// Call Stringer/error interfaces if they exist and the handle methods
 | 
			
		||||
	// flag is enabled.
 | 
			
		||||
	if !f.cs.DisableMethods {
 | 
			
		||||
		if (kind != reflect.Invalid) && (kind != reflect.Interface) {
 | 
			
		||||
			if handled := handleMethods(f.cs, f.fs, v); handled {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch kind {
 | 
			
		||||
	case reflect.Invalid:
 | 
			
		||||
		// Do nothing.  We should never get here since invalid has already
 | 
			
		||||
		// been handled above.
 | 
			
		||||
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		printBool(f.fs, v.Bool())
 | 
			
		||||
 | 
			
		||||
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 | 
			
		||||
		printInt(f.fs, v.Int(), 10)
 | 
			
		||||
 | 
			
		||||
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
 | 
			
		||||
		printUint(f.fs, v.Uint(), 10)
 | 
			
		||||
 | 
			
		||||
	case reflect.Float32:
 | 
			
		||||
		printFloat(f.fs, v.Float(), 32)
 | 
			
		||||
 | 
			
		||||
	case reflect.Float64:
 | 
			
		||||
		printFloat(f.fs, v.Float(), 64)
 | 
			
		||||
 | 
			
		||||
	case reflect.Complex64:
 | 
			
		||||
		printComplex(f.fs, v.Complex(), 32)
 | 
			
		||||
 | 
			
		||||
	case reflect.Complex128:
 | 
			
		||||
		printComplex(f.fs, v.Complex(), 64)
 | 
			
		||||
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			f.fs.Write(nilAngleBytes)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		fallthrough
 | 
			
		||||
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		f.fs.Write(openBracketBytes)
 | 
			
		||||
		f.depth++
 | 
			
		||||
		if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
 | 
			
		||||
			f.fs.Write(maxShortBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			numEntries := v.Len()
 | 
			
		||||
			for i := 0; i < numEntries; i++ {
 | 
			
		||||
				if i > 0 {
 | 
			
		||||
					f.fs.Write(spaceBytes)
 | 
			
		||||
				}
 | 
			
		||||
				f.ignoreNextType = true
 | 
			
		||||
				f.format(f.unpackValue(v.Index(i)))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		f.depth--
 | 
			
		||||
		f.fs.Write(closeBracketBytes)
 | 
			
		||||
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		f.fs.Write([]byte(v.String()))
 | 
			
		||||
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		// The only time we should get here is for nil interfaces due to
 | 
			
		||||
		// unpackValue calls.
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			f.fs.Write(nilAngleBytes)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		// Do nothing.  We should never get here since pointers have already
 | 
			
		||||
		// been handled above.
 | 
			
		||||
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		// nil maps should be indicated as different than empty maps
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			f.fs.Write(nilAngleBytes)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		f.fs.Write(openMapBytes)
 | 
			
		||||
		f.depth++
 | 
			
		||||
		if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
 | 
			
		||||
			f.fs.Write(maxShortBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			keys := v.MapKeys()
 | 
			
		||||
			if f.cs.SortKeys {
 | 
			
		||||
				sortValues(keys, f.cs)
 | 
			
		||||
			}
 | 
			
		||||
			for i, key := range keys {
 | 
			
		||||
				if i > 0 {
 | 
			
		||||
					f.fs.Write(spaceBytes)
 | 
			
		||||
				}
 | 
			
		||||
				f.ignoreNextType = true
 | 
			
		||||
				f.format(f.unpackValue(key))
 | 
			
		||||
				f.fs.Write(colonBytes)
 | 
			
		||||
				f.ignoreNextType = true
 | 
			
		||||
				f.format(f.unpackValue(v.MapIndex(key)))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		f.depth--
 | 
			
		||||
		f.fs.Write(closeMapBytes)
 | 
			
		||||
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		numFields := v.NumField()
 | 
			
		||||
		f.fs.Write(openBraceBytes)
 | 
			
		||||
		f.depth++
 | 
			
		||||
		if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
 | 
			
		||||
			f.fs.Write(maxShortBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			vt := v.Type()
 | 
			
		||||
			for i := 0; i < numFields; i++ {
 | 
			
		||||
				if i > 0 {
 | 
			
		||||
					f.fs.Write(spaceBytes)
 | 
			
		||||
				}
 | 
			
		||||
				vtf := vt.Field(i)
 | 
			
		||||
				if f.fs.Flag('+') || f.fs.Flag('#') {
 | 
			
		||||
					f.fs.Write([]byte(vtf.Name))
 | 
			
		||||
					f.fs.Write(colonBytes)
 | 
			
		||||
				}
 | 
			
		||||
				f.format(f.unpackValue(v.Field(i)))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		f.depth--
 | 
			
		||||
		f.fs.Write(closeBraceBytes)
 | 
			
		||||
 | 
			
		||||
	case reflect.Uintptr:
 | 
			
		||||
		printHexPtr(f.fs, uintptr(v.Uint()))
 | 
			
		||||
 | 
			
		||||
	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
 | 
			
		||||
		printHexPtr(f.fs, v.Pointer())
 | 
			
		||||
 | 
			
		||||
	// There were not any other types at the time this code was written, but
 | 
			
		||||
	// fall back to letting the default fmt package handle it if any get added.
 | 
			
		||||
	default:
 | 
			
		||||
		format := f.buildDefaultFormat()
 | 
			
		||||
		if v.CanInterface() {
 | 
			
		||||
			fmt.Fprintf(f.fs, format, v.Interface())
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(f.fs, format, v.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
 | 
			
		||||
// details.
 | 
			
		||||
func (f *formatState) Format(fs fmt.State, verb rune) {
 | 
			
		||||
	f.fs = fs
 | 
			
		||||
 | 
			
		||||
	// Use standard formatting for verbs that are not v.
 | 
			
		||||
	if verb != 'v' {
 | 
			
		||||
		format := f.constructOrigFormat(verb)
 | 
			
		||||
		fmt.Fprintf(fs, format, f.value)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.value == nil {
 | 
			
		||||
		if fs.Flag('#') {
 | 
			
		||||
			fs.Write(interfaceBytes)
 | 
			
		||||
		}
 | 
			
		||||
		fs.Write(nilAngleBytes)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f.format(reflect.ValueOf(f.value))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newFormatter is a helper function to consolidate the logic from the various
 | 
			
		||||
// public methods which take varying config states.
 | 
			
		||||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
 | 
			
		||||
	fs := &formatState{value: v, cs: cs}
 | 
			
		||||
	fs.pointers = make(map[uintptr]int)
 | 
			
		||||
	return fs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
 | 
			
		||||
interface.  As a result, it integrates cleanly with standard fmt package
 | 
			
		||||
printing functions.  The formatter is useful for inline printing of smaller data
 | 
			
		||||
types similar to the standard %v format specifier.
 | 
			
		||||
 | 
			
		||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
 | 
			
		||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
 | 
			
		||||
combinations.  Any other verbs such as %x and %q will be sent to the the
 | 
			
		||||
standard fmt package for formatting.  In addition, the custom formatter ignores
 | 
			
		||||
the width and precision arguments (however they will still work on the format
 | 
			
		||||
specifiers not handled by the custom formatter).
 | 
			
		||||
 | 
			
		||||
Typically this function shouldn't be called directly.  It is much easier to make
 | 
			
		||||
use of the custom formatter by calling one of the convenience functions such as
 | 
			
		||||
Printf, Println, or Fprintf.
 | 
			
		||||
*/
 | 
			
		||||
func NewFormatter(v interface{}) fmt.Formatter {
 | 
			
		||||
	return newFormatter(&Config, v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								vendor/github.com/davecgh/go-spew/spew/spew.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										148
									
								
								vendor/github.com/davecgh/go-spew/spew/spew.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,148 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package spew
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the formatted string as a value that satisfies error.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Errorf(format string, a ...interface{}) (err error) {
 | 
			
		||||
	return fmt.Errorf(format, convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Fprint(w, convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintf(w, format, convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintln(w, convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Print(a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Print(convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Printf(format string, a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Printf(format, convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the number of bytes written and any write error encountered.  See
 | 
			
		||||
// NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Println(a ...interface{}) (n int, err error) {
 | 
			
		||||
	return fmt.Println(convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the resulting string.  See NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Sprint(a ...interface{}) string {
 | 
			
		||||
	return fmt.Sprint(convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
 | 
			
		||||
// passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the resulting string.  See NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Sprintf(format string, a ...interface{}) string {
 | 
			
		||||
	return fmt.Sprintf(format, convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
 | 
			
		||||
// were passed with a default Formatter interface returned by NewFormatter.  It
 | 
			
		||||
// returns the resulting string.  See NewFormatter for formatting details.
 | 
			
		||||
//
 | 
			
		||||
// This function is shorthand for the following syntax:
 | 
			
		||||
//
 | 
			
		||||
//	fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
 | 
			
		||||
func Sprintln(a ...interface{}) string {
 | 
			
		||||
	return fmt.Sprintln(convertArgs(a)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertArgs accepts a slice of arguments and returns a slice of the same
 | 
			
		||||
// length with each argument converted to a default spew Formatter interface.
 | 
			
		||||
func convertArgs(args []interface{}) (formatters []interface{}) {
 | 
			
		||||
	formatters = make([]interface{}, len(args))
 | 
			
		||||
	for index, arg := range args {
 | 
			
		||||
		formatters[index] = NewFormatter(arg)
 | 
			
		||||
	}
 | 
			
		||||
	return formatters
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/github.com/fatedier/beego/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/fatedier/beego/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,13 +0,0 @@
 | 
			
		||||
Copyright 2014 astaxie
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										63
									
								
								vendor/github.com/fatedier/beego/logs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/fatedier/beego/logs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,63 +0,0 @@
 | 
			
		||||
## logs
 | 
			
		||||
logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` .
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## How to install?
 | 
			
		||||
 | 
			
		||||
	go get github.com/astaxie/beego/logs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## What adapters are supported?
 | 
			
		||||
 | 
			
		||||
As of now this logs support console, file,smtp and conn.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## How to use it?
 | 
			
		||||
 | 
			
		||||
First you must import it
 | 
			
		||||
 | 
			
		||||
	import (
 | 
			
		||||
		"github.com/astaxie/beego/logs"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
Then init a Log (example with console adapter)
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(10000)
 | 
			
		||||
	log.SetLogger("console", "")	
 | 
			
		||||
 | 
			
		||||
> the first params stand for how many channel
 | 
			
		||||
 | 
			
		||||
Use it like this:	
 | 
			
		||||
	
 | 
			
		||||
	log.Trace("trace")
 | 
			
		||||
	log.Info("info")
 | 
			
		||||
	log.Warn("warning")
 | 
			
		||||
	log.Debug("debug")
 | 
			
		||||
	log.Critical("critical")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## File adapter
 | 
			
		||||
 | 
			
		||||
Configure file adapter like this:
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(10000)
 | 
			
		||||
	log.SetLogger("file", `{"filename":"test.log"}`)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Conn adapter
 | 
			
		||||
 | 
			
		||||
Configure like this:
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(1000)
 | 
			
		||||
	log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
 | 
			
		||||
	log.Info("info")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Smtp adapter
 | 
			
		||||
 | 
			
		||||
Configure like this:
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(10000)
 | 
			
		||||
	log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
 | 
			
		||||
	log.Critical("sendmail critical")
 | 
			
		||||
	time.Sleep(time.Second * 30)
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/github.com/fatedier/beego/logs/color.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/fatedier/beego/logs/color.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,28 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
// +build !windows
 | 
			
		||||
 | 
			
		||||
package logs
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
type ansiColorWriter struct {
 | 
			
		||||
	w    io.Writer
 | 
			
		||||
	mode outputMode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
 | 
			
		||||
	return cw.w.Write(p)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										428
									
								
								vendor/github.com/fatedier/beego/logs/color_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										428
									
								
								vendor/github.com/fatedier/beego/logs/color_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,428 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
// +build windows
 | 
			
		||||
 | 
			
		||||
package logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	csiState    int
 | 
			
		||||
	parseResult int
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	outsideCsiCode csiState = iota
 | 
			
		||||
	firstCsiCode
 | 
			
		||||
	secondCsiCode
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	noConsole parseResult = iota
 | 
			
		||||
	changedColor
 | 
			
		||||
	unknown
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ansiColorWriter struct {
 | 
			
		||||
	w             io.Writer
 | 
			
		||||
	mode          outputMode
 | 
			
		||||
	state         csiState
 | 
			
		||||
	paramStartBuf bytes.Buffer
 | 
			
		||||
	paramBuf      bytes.Buffer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	firstCsiChar   byte = '\x1b'
 | 
			
		||||
	secondeCsiChar byte = '['
 | 
			
		||||
	separatorChar  byte = ';'
 | 
			
		||||
	sgrCode        byte = 'm'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	foregroundBlue      = uint16(0x0001)
 | 
			
		||||
	foregroundGreen     = uint16(0x0002)
 | 
			
		||||
	foregroundRed       = uint16(0x0004)
 | 
			
		||||
	foregroundIntensity = uint16(0x0008)
 | 
			
		||||
	backgroundBlue      = uint16(0x0010)
 | 
			
		||||
	backgroundGreen     = uint16(0x0020)
 | 
			
		||||
	backgroundRed       = uint16(0x0040)
 | 
			
		||||
	backgroundIntensity = uint16(0x0080)
 | 
			
		||||
	underscore          = uint16(0x8000)
 | 
			
		||||
 | 
			
		||||
	foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
 | 
			
		||||
	backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ansiReset        = "0"
 | 
			
		||||
	ansiIntensityOn  = "1"
 | 
			
		||||
	ansiIntensityOff = "21"
 | 
			
		||||
	ansiUnderlineOn  = "4"
 | 
			
		||||
	ansiUnderlineOff = "24"
 | 
			
		||||
	ansiBlinkOn      = "5"
 | 
			
		||||
	ansiBlinkOff     = "25"
 | 
			
		||||
 | 
			
		||||
	ansiForegroundBlack   = "30"
 | 
			
		||||
	ansiForegroundRed     = "31"
 | 
			
		||||
	ansiForegroundGreen   = "32"
 | 
			
		||||
	ansiForegroundYellow  = "33"
 | 
			
		||||
	ansiForegroundBlue    = "34"
 | 
			
		||||
	ansiForegroundMagenta = "35"
 | 
			
		||||
	ansiForegroundCyan    = "36"
 | 
			
		||||
	ansiForegroundWhite   = "37"
 | 
			
		||||
	ansiForegroundDefault = "39"
 | 
			
		||||
 | 
			
		||||
	ansiBackgroundBlack   = "40"
 | 
			
		||||
	ansiBackgroundRed     = "41"
 | 
			
		||||
	ansiBackgroundGreen   = "42"
 | 
			
		||||
	ansiBackgroundYellow  = "43"
 | 
			
		||||
	ansiBackgroundBlue    = "44"
 | 
			
		||||
	ansiBackgroundMagenta = "45"
 | 
			
		||||
	ansiBackgroundCyan    = "46"
 | 
			
		||||
	ansiBackgroundWhite   = "47"
 | 
			
		||||
	ansiBackgroundDefault = "49"
 | 
			
		||||
 | 
			
		||||
	ansiLightForegroundGray    = "90"
 | 
			
		||||
	ansiLightForegroundRed     = "91"
 | 
			
		||||
	ansiLightForegroundGreen   = "92"
 | 
			
		||||
	ansiLightForegroundYellow  = "93"
 | 
			
		||||
	ansiLightForegroundBlue    = "94"
 | 
			
		||||
	ansiLightForegroundMagenta = "95"
 | 
			
		||||
	ansiLightForegroundCyan    = "96"
 | 
			
		||||
	ansiLightForegroundWhite   = "97"
 | 
			
		||||
 | 
			
		||||
	ansiLightBackgroundGray    = "100"
 | 
			
		||||
	ansiLightBackgroundRed     = "101"
 | 
			
		||||
	ansiLightBackgroundGreen   = "102"
 | 
			
		||||
	ansiLightBackgroundYellow  = "103"
 | 
			
		||||
	ansiLightBackgroundBlue    = "104"
 | 
			
		||||
	ansiLightBackgroundMagenta = "105"
 | 
			
		||||
	ansiLightBackgroundCyan    = "106"
 | 
			
		||||
	ansiLightBackgroundWhite   = "107"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type drawType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	foreground drawType = iota
 | 
			
		||||
	background
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type winColor struct {
 | 
			
		||||
	code     uint16
 | 
			
		||||
	drawType drawType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var colorMap = map[string]winColor{
 | 
			
		||||
	ansiForegroundBlack:   {0, foreground},
 | 
			
		||||
	ansiForegroundRed:     {foregroundRed, foreground},
 | 
			
		||||
	ansiForegroundGreen:   {foregroundGreen, foreground},
 | 
			
		||||
	ansiForegroundYellow:  {foregroundRed | foregroundGreen, foreground},
 | 
			
		||||
	ansiForegroundBlue:    {foregroundBlue, foreground},
 | 
			
		||||
	ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
 | 
			
		||||
	ansiForegroundCyan:    {foregroundGreen | foregroundBlue, foreground},
 | 
			
		||||
	ansiForegroundWhite:   {foregroundRed | foregroundGreen | foregroundBlue, foreground},
 | 
			
		||||
	ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
 | 
			
		||||
 | 
			
		||||
	ansiBackgroundBlack:   {0, background},
 | 
			
		||||
	ansiBackgroundRed:     {backgroundRed, background},
 | 
			
		||||
	ansiBackgroundGreen:   {backgroundGreen, background},
 | 
			
		||||
	ansiBackgroundYellow:  {backgroundRed | backgroundGreen, background},
 | 
			
		||||
	ansiBackgroundBlue:    {backgroundBlue, background},
 | 
			
		||||
	ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
 | 
			
		||||
	ansiBackgroundCyan:    {backgroundGreen | backgroundBlue, background},
 | 
			
		||||
	ansiBackgroundWhite:   {backgroundRed | backgroundGreen | backgroundBlue, background},
 | 
			
		||||
	ansiBackgroundDefault: {0, background},
 | 
			
		||||
 | 
			
		||||
	ansiLightForegroundGray:    {foregroundIntensity, foreground},
 | 
			
		||||
	ansiLightForegroundRed:     {foregroundIntensity | foregroundRed, foreground},
 | 
			
		||||
	ansiLightForegroundGreen:   {foregroundIntensity | foregroundGreen, foreground},
 | 
			
		||||
	ansiLightForegroundYellow:  {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
 | 
			
		||||
	ansiLightForegroundBlue:    {foregroundIntensity | foregroundBlue, foreground},
 | 
			
		||||
	ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
 | 
			
		||||
	ansiLightForegroundCyan:    {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
 | 
			
		||||
	ansiLightForegroundWhite:   {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},
 | 
			
		||||
 | 
			
		||||
	ansiLightBackgroundGray:    {backgroundIntensity, background},
 | 
			
		||||
	ansiLightBackgroundRed:     {backgroundIntensity | backgroundRed, background},
 | 
			
		||||
	ansiLightBackgroundGreen:   {backgroundIntensity | backgroundGreen, background},
 | 
			
		||||
	ansiLightBackgroundYellow:  {backgroundIntensity | backgroundRed | backgroundGreen, background},
 | 
			
		||||
	ansiLightBackgroundBlue:    {backgroundIntensity | backgroundBlue, background},
 | 
			
		||||
	ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
 | 
			
		||||
	ansiLightBackgroundCyan:    {backgroundIntensity | backgroundGreen | backgroundBlue, background},
 | 
			
		||||
	ansiLightBackgroundWhite:   {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
 | 
			
		||||
	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
 | 
			
		||||
	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
 | 
			
		||||
	defaultAttr                    *textAttributes
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
 | 
			
		||||
	if screenInfo != nil {
 | 
			
		||||
		colorMap[ansiForegroundDefault] = winColor{
 | 
			
		||||
			screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
 | 
			
		||||
			foreground,
 | 
			
		||||
		}
 | 
			
		||||
		colorMap[ansiBackgroundDefault] = winColor{
 | 
			
		||||
			screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
 | 
			
		||||
			background,
 | 
			
		||||
		}
 | 
			
		||||
		defaultAttr = convertTextAttr(screenInfo.WAttributes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type coord struct {
 | 
			
		||||
	X, Y int16
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type smallRect struct {
 | 
			
		||||
	Left, Top, Right, Bottom int16
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type consoleScreenBufferInfo struct {
 | 
			
		||||
	DwSize              coord
 | 
			
		||||
	DwCursorPosition    coord
 | 
			
		||||
	WAttributes         uint16
 | 
			
		||||
	SrWindow            smallRect
 | 
			
		||||
	DwMaximumWindowSize coord
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
 | 
			
		||||
	var csbi consoleScreenBufferInfo
 | 
			
		||||
	ret, _, _ := procGetConsoleScreenBufferInfo.Call(
 | 
			
		||||
		hConsoleOutput,
 | 
			
		||||
		uintptr(unsafe.Pointer(&csbi)))
 | 
			
		||||
	if ret == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return &csbi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
 | 
			
		||||
	ret, _, _ := procSetConsoleTextAttribute.Call(
 | 
			
		||||
		hConsoleOutput,
 | 
			
		||||
		uintptr(wAttributes))
 | 
			
		||||
	return ret != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type textAttributes struct {
 | 
			
		||||
	foregroundColor     uint16
 | 
			
		||||
	backgroundColor     uint16
 | 
			
		||||
	foregroundIntensity uint16
 | 
			
		||||
	backgroundIntensity uint16
 | 
			
		||||
	underscore          uint16
 | 
			
		||||
	otherAttributes     uint16
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertTextAttr(winAttr uint16) *textAttributes {
 | 
			
		||||
	fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
 | 
			
		||||
	bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
 | 
			
		||||
	fgIntensity := winAttr & foregroundIntensity
 | 
			
		||||
	bgIntensity := winAttr & backgroundIntensity
 | 
			
		||||
	underline := winAttr & underscore
 | 
			
		||||
	otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
 | 
			
		||||
	return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertWinAttr(textAttr *textAttributes) uint16 {
 | 
			
		||||
	var winAttr uint16
 | 
			
		||||
	winAttr |= textAttr.foregroundColor
 | 
			
		||||
	winAttr |= textAttr.backgroundColor
 | 
			
		||||
	winAttr |= textAttr.foregroundIntensity
 | 
			
		||||
	winAttr |= textAttr.backgroundIntensity
 | 
			
		||||
	winAttr |= textAttr.underscore
 | 
			
		||||
	winAttr |= textAttr.otherAttributes
 | 
			
		||||
	return winAttr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func changeColor(param []byte) parseResult {
 | 
			
		||||
	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
 | 
			
		||||
	if screenInfo == nil {
 | 
			
		||||
		return noConsole
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	winAttr := convertTextAttr(screenInfo.WAttributes)
 | 
			
		||||
	strParam := string(param)
 | 
			
		||||
	if len(strParam) <= 0 {
 | 
			
		||||
		strParam = "0"
 | 
			
		||||
	}
 | 
			
		||||
	csiParam := strings.Split(strParam, string(separatorChar))
 | 
			
		||||
	for _, p := range csiParam {
 | 
			
		||||
		c, ok := colorMap[p]
 | 
			
		||||
		switch {
 | 
			
		||||
		case !ok:
 | 
			
		||||
			switch p {
 | 
			
		||||
			case ansiReset:
 | 
			
		||||
				winAttr.foregroundColor = defaultAttr.foregroundColor
 | 
			
		||||
				winAttr.backgroundColor = defaultAttr.backgroundColor
 | 
			
		||||
				winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
 | 
			
		||||
				winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
 | 
			
		||||
				winAttr.underscore = 0
 | 
			
		||||
				winAttr.otherAttributes = 0
 | 
			
		||||
			case ansiIntensityOn:
 | 
			
		||||
				winAttr.foregroundIntensity = foregroundIntensity
 | 
			
		||||
			case ansiIntensityOff:
 | 
			
		||||
				winAttr.foregroundIntensity = 0
 | 
			
		||||
			case ansiUnderlineOn:
 | 
			
		||||
				winAttr.underscore = underscore
 | 
			
		||||
			case ansiUnderlineOff:
 | 
			
		||||
				winAttr.underscore = 0
 | 
			
		||||
			case ansiBlinkOn:
 | 
			
		||||
				winAttr.backgroundIntensity = backgroundIntensity
 | 
			
		||||
			case ansiBlinkOff:
 | 
			
		||||
				winAttr.backgroundIntensity = 0
 | 
			
		||||
			default:
 | 
			
		||||
				// unknown code
 | 
			
		||||
			}
 | 
			
		||||
		case c.drawType == foreground:
 | 
			
		||||
			winAttr.foregroundColor = c.code
 | 
			
		||||
		case c.drawType == background:
 | 
			
		||||
			winAttr.backgroundColor = c.code
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	winTextAttribute := convertWinAttr(winAttr)
 | 
			
		||||
	setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)
 | 
			
		||||
 | 
			
		||||
	return changedColor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseEscapeSequence(command byte, param []byte) parseResult {
 | 
			
		||||
	if defaultAttr == nil {
 | 
			
		||||
		return noConsole
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch command {
 | 
			
		||||
	case sgrCode:
 | 
			
		||||
		return changeColor(param)
 | 
			
		||||
	default:
 | 
			
		||||
		return unknown
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cw *ansiColorWriter) flushBuffer() (int, error) {
 | 
			
		||||
	return cw.flushTo(cw.w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cw *ansiColorWriter) resetBuffer() (int, error) {
 | 
			
		||||
	return cw.flushTo(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) {
 | 
			
		||||
	var n1, n2 int
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	startBytes := cw.paramStartBuf.Bytes()
 | 
			
		||||
	cw.paramStartBuf.Reset()
 | 
			
		||||
	if w != nil {
 | 
			
		||||
		n1, err = cw.w.Write(startBytes)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return n1, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		n1 = len(startBytes)
 | 
			
		||||
	}
 | 
			
		||||
	paramBytes := cw.paramBuf.Bytes()
 | 
			
		||||
	cw.paramBuf.Reset()
 | 
			
		||||
	if w != nil {
 | 
			
		||||
		n2, err = cw.w.Write(paramBytes)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return n1 + n2, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		n2 = len(paramBytes)
 | 
			
		||||
	}
 | 
			
		||||
	return n1 + n2, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isParameterChar(b byte) bool {
 | 
			
		||||
	return ('0' <= b && b <= '9') || b == separatorChar
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
 | 
			
		||||
	r, nw, first, last := 0, 0, 0, 0
 | 
			
		||||
	if cw.mode != DiscardNonColorEscSeq {
 | 
			
		||||
		cw.state = outsideCsiCode
 | 
			
		||||
		cw.resetBuffer()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	for i, ch := range p {
 | 
			
		||||
		switch cw.state {
 | 
			
		||||
		case outsideCsiCode:
 | 
			
		||||
			if ch == firstCsiChar {
 | 
			
		||||
				cw.paramStartBuf.WriteByte(ch)
 | 
			
		||||
				cw.state = firstCsiCode
 | 
			
		||||
			}
 | 
			
		||||
		case firstCsiCode:
 | 
			
		||||
			switch ch {
 | 
			
		||||
			case firstCsiChar:
 | 
			
		||||
				cw.paramStartBuf.WriteByte(ch)
 | 
			
		||||
				break
 | 
			
		||||
			case secondeCsiChar:
 | 
			
		||||
				cw.paramStartBuf.WriteByte(ch)
 | 
			
		||||
				cw.state = secondCsiCode
 | 
			
		||||
				last = i - 1
 | 
			
		||||
			default:
 | 
			
		||||
				cw.resetBuffer()
 | 
			
		||||
				cw.state = outsideCsiCode
 | 
			
		||||
			}
 | 
			
		||||
		case secondCsiCode:
 | 
			
		||||
			if isParameterChar(ch) {
 | 
			
		||||
				cw.paramBuf.WriteByte(ch)
 | 
			
		||||
			} else {
 | 
			
		||||
				nw, err = cw.w.Write(p[first:last])
 | 
			
		||||
				r += nw
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return r, err
 | 
			
		||||
				}
 | 
			
		||||
				first = i + 1
 | 
			
		||||
				result := parseEscapeSequence(ch, cw.paramBuf.Bytes())
 | 
			
		||||
				if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) {
 | 
			
		||||
					cw.paramBuf.WriteByte(ch)
 | 
			
		||||
					nw, err := cw.flushBuffer()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return r, err
 | 
			
		||||
					}
 | 
			
		||||
					r += nw
 | 
			
		||||
				} else {
 | 
			
		||||
					n, _ := cw.resetBuffer()
 | 
			
		||||
					// Add one more to the size of the buffer for the last ch
 | 
			
		||||
					r += n + 1
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				cw.state = outsideCsiCode
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			cw.state = outsideCsiCode
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
 | 
			
		||||
		nw, err = cw.w.Write(p[first:])
 | 
			
		||||
		r += nw
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										117
									
								
								vendor/github.com/fatedier/beego/logs/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/fatedier/beego/logs/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,117 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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 logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// connWriter implements LoggerInterface.
 | 
			
		||||
// it writes messages in keep-live tcp connection.
 | 
			
		||||
type connWriter struct {
 | 
			
		||||
	lg             *logWriter
 | 
			
		||||
	innerWriter    io.WriteCloser
 | 
			
		||||
	ReconnectOnMsg bool   `json:"reconnectOnMsg"`
 | 
			
		||||
	Reconnect      bool   `json:"reconnect"`
 | 
			
		||||
	Net            string `json:"net"`
 | 
			
		||||
	Addr           string `json:"addr"`
 | 
			
		||||
	Level          int    `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewConn create new ConnWrite returning as LoggerInterface.
 | 
			
		||||
func NewConn() Logger {
 | 
			
		||||
	conn := new(connWriter)
 | 
			
		||||
	conn.Level = LevelTrace
 | 
			
		||||
	return conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init init connection writer with json config.
 | 
			
		||||
// json config only need key "level".
 | 
			
		||||
func (c *connWriter) Init(jsonConfig string) error {
 | 
			
		||||
	return json.Unmarshal([]byte(jsonConfig), c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMsg write message in connection.
 | 
			
		||||
// if connection is down, try to re-connect.
 | 
			
		||||
func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error {
 | 
			
		||||
	if level > c.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if c.needToConnectOnMsg() {
 | 
			
		||||
		err := c.connect()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.ReconnectOnMsg {
 | 
			
		||||
		defer c.innerWriter.Close()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.lg.println(when, msg)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flush implementing method. empty.
 | 
			
		||||
func (c *connWriter) Flush() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy destroy connection writer and close tcp listener.
 | 
			
		||||
func (c *connWriter) Destroy() {
 | 
			
		||||
	if c.innerWriter != nil {
 | 
			
		||||
		c.innerWriter.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *connWriter) connect() error {
 | 
			
		||||
	if c.innerWriter != nil {
 | 
			
		||||
		c.innerWriter.Close()
 | 
			
		||||
		c.innerWriter = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, err := net.Dial(c.Net, c.Addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tcpConn, ok := conn.(*net.TCPConn); ok {
 | 
			
		||||
		tcpConn.SetKeepAlive(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.innerWriter = conn
 | 
			
		||||
	c.lg = newLogWriter(conn)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *connWriter) needToConnectOnMsg() bool {
 | 
			
		||||
	if c.Reconnect {
 | 
			
		||||
		c.Reconnect = false
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.innerWriter == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.ReconnectOnMsg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register(AdapterConn, NewConn)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										102
									
								
								vendor/github.com/fatedier/beego/logs/console.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/fatedier/beego/logs/console.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,102 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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 logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// brush is a color join function
 | 
			
		||||
type brush func(string) string
 | 
			
		||||
 | 
			
		||||
// newBrush return a fix color Brush
 | 
			
		||||
func newBrush(color string) brush {
 | 
			
		||||
	pre := "\033["
 | 
			
		||||
	reset := "\033[0m"
 | 
			
		||||
	return func(text string) string {
 | 
			
		||||
		return pre + color + "m" + text + reset
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var colors = []brush{
 | 
			
		||||
	newBrush("1;37"), // Emergency          white
 | 
			
		||||
	newBrush("1;36"), // Alert              cyan
 | 
			
		||||
	newBrush("1;35"), // Critical           magenta
 | 
			
		||||
	newBrush("1;31"), // Error              red
 | 
			
		||||
	newBrush("1;33"), // Warning            yellow
 | 
			
		||||
	newBrush("1;32"), // Notice             green
 | 
			
		||||
	newBrush("1;34"), // Informational      blue
 | 
			
		||||
	newBrush("1;34"), // Debug              blue
 | 
			
		||||
	newBrush("1;34"), // Trace              blue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// consoleWriter implements LoggerInterface and writes messages to terminal.
 | 
			
		||||
type consoleWriter struct {
 | 
			
		||||
	lg       *logWriter
 | 
			
		||||
	Level    int  `json:"level"`
 | 
			
		||||
	Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewConsole create ConsoleWriter returning as LoggerInterface.
 | 
			
		||||
func NewConsole() Logger {
 | 
			
		||||
	cw := &consoleWriter{
 | 
			
		||||
		lg:       newLogWriter(os.Stdout),
 | 
			
		||||
		Level:    LevelTrace,
 | 
			
		||||
		Colorful: runtime.GOOS != "windows",
 | 
			
		||||
	}
 | 
			
		||||
	return cw
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init init console logger.
 | 
			
		||||
// jsonConfig like '{"level":LevelTrace}'.
 | 
			
		||||
func (c *consoleWriter) Init(jsonConfig string) error {
 | 
			
		||||
	if len(jsonConfig) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonConfig), c)
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		c.Colorful = false
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMsg write message in console.
 | 
			
		||||
func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error {
 | 
			
		||||
	if level > c.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if c.Colorful {
 | 
			
		||||
		msg = colors[level](msg)
 | 
			
		||||
	}
 | 
			
		||||
	c.lg.println(when, msg)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy implementing method. empty.
 | 
			
		||||
func (c *consoleWriter) Destroy() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flush implementing method. empty.
 | 
			
		||||
func (c *consoleWriter) Flush() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register(AdapterConsole, NewConsole)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										327
									
								
								vendor/github.com/fatedier/beego/logs/file.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										327
									
								
								vendor/github.com/fatedier/beego/logs/file.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,327 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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 logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// fileLogWriter implements LoggerInterface.
 | 
			
		||||
// It writes messages by lines limit, file size limit, or time frequency.
 | 
			
		||||
type fileLogWriter struct {
 | 
			
		||||
	sync.RWMutex // write log order by order and  atomic incr maxLinesCurLines and maxSizeCurSize
 | 
			
		||||
	// The opened file
 | 
			
		||||
	Filename   string `json:"filename"`
 | 
			
		||||
	fileWriter *os.File
 | 
			
		||||
 | 
			
		||||
	// Rotate at line
 | 
			
		||||
	MaxLines         int `json:"maxlines"`
 | 
			
		||||
	maxLinesCurLines int
 | 
			
		||||
 | 
			
		||||
	// Rotate at size
 | 
			
		||||
	MaxSize        int `json:"maxsize"`
 | 
			
		||||
	maxSizeCurSize int
 | 
			
		||||
 | 
			
		||||
	// Rotate daily
 | 
			
		||||
	Daily         bool  `json:"daily"`
 | 
			
		||||
	MaxDays       int64 `json:"maxdays"`
 | 
			
		||||
	dailyOpenDate int
 | 
			
		||||
	dailyOpenTime time.Time
 | 
			
		||||
 | 
			
		||||
	Rotate bool `json:"rotate"`
 | 
			
		||||
 | 
			
		||||
	Level int `json:"level"`
 | 
			
		||||
 | 
			
		||||
	Perm string `json:"perm"`
 | 
			
		||||
 | 
			
		||||
	fileNameOnly, suffix string // like "project.log", project is fileNameOnly and .log is suffix
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newFileWriter create a FileLogWriter returning as LoggerInterface.
 | 
			
		||||
func newFileWriter() Logger {
 | 
			
		||||
	w := &fileLogWriter{
 | 
			
		||||
		Daily:   true,
 | 
			
		||||
		MaxDays: 7,
 | 
			
		||||
		Rotate:  true,
 | 
			
		||||
		Level:   LevelTrace,
 | 
			
		||||
		Perm:    "0660",
 | 
			
		||||
	}
 | 
			
		||||
	return w
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init file logger with json config.
 | 
			
		||||
// jsonConfig like:
 | 
			
		||||
//	{
 | 
			
		||||
//	"filename":"logs/beego.log",
 | 
			
		||||
//	"maxLines":10000,
 | 
			
		||||
//	"maxsize":1024,
 | 
			
		||||
//	"daily":true,
 | 
			
		||||
//	"maxDays":15,
 | 
			
		||||
//	"rotate":true,
 | 
			
		||||
//  	"perm":"0600"
 | 
			
		||||
//	}
 | 
			
		||||
func (w *fileLogWriter) Init(jsonConfig string) error {
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonConfig), w)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if len(w.Filename) == 0 {
 | 
			
		||||
		return errors.New("jsonconfig must have filename")
 | 
			
		||||
	}
 | 
			
		||||
	w.suffix = filepath.Ext(w.Filename)
 | 
			
		||||
	w.fileNameOnly = strings.TrimSuffix(w.Filename, w.suffix)
 | 
			
		||||
	if w.suffix == "" {
 | 
			
		||||
		w.suffix = ".log"
 | 
			
		||||
	}
 | 
			
		||||
	err = w.startLogger()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// start file logger. create log file and set to locker-inside file writer.
 | 
			
		||||
func (w *fileLogWriter) startLogger() error {
 | 
			
		||||
	file, err := w.createLogFile()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if w.fileWriter != nil {
 | 
			
		||||
		w.fileWriter.Close()
 | 
			
		||||
	}
 | 
			
		||||
	w.fileWriter = file
 | 
			
		||||
	return w.initFd()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *fileLogWriter) needRotate(size int, day int) bool {
 | 
			
		||||
	return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) ||
 | 
			
		||||
		(w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) ||
 | 
			
		||||
		(w.Daily && day != w.dailyOpenDate)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMsg write logger message into file.
 | 
			
		||||
func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error {
 | 
			
		||||
	if level > w.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	h, d := formatTimeHeader(when)
 | 
			
		||||
	msg = string(h) + msg + "\n"
 | 
			
		||||
	if w.Rotate {
 | 
			
		||||
		w.RLock()
 | 
			
		||||
		if w.needRotate(len(msg), d) {
 | 
			
		||||
			w.RUnlock()
 | 
			
		||||
			w.Lock()
 | 
			
		||||
			if w.needRotate(len(msg), d) {
 | 
			
		||||
				if err := w.doRotate(when); err != nil {
 | 
			
		||||
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			w.Unlock()
 | 
			
		||||
		} else {
 | 
			
		||||
			w.RUnlock()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Lock()
 | 
			
		||||
	_, err := w.fileWriter.Write([]byte(msg))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		w.maxLinesCurLines++
 | 
			
		||||
		w.maxSizeCurSize += len(msg)
 | 
			
		||||
	}
 | 
			
		||||
	w.Unlock()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *fileLogWriter) createLogFile() (*os.File, error) {
 | 
			
		||||
	// Open the log file
 | 
			
		||||
	perm, err := strconv.ParseInt(w.Perm, 8, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		// Make sure file perm is user set perm cause of `os.OpenFile` will obey umask
 | 
			
		||||
		os.Chmod(w.Filename, os.FileMode(perm))
 | 
			
		||||
	}
 | 
			
		||||
	return fd, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *fileLogWriter) initFd() error {
 | 
			
		||||
	fd := w.fileWriter
 | 
			
		||||
	fInfo, err := fd.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("get stat err: %s\n", err)
 | 
			
		||||
	}
 | 
			
		||||
	w.maxSizeCurSize = int(fInfo.Size())
 | 
			
		||||
	w.dailyOpenTime = time.Now()
 | 
			
		||||
	w.dailyOpenDate = w.dailyOpenTime.Day()
 | 
			
		||||
	w.maxLinesCurLines = 0
 | 
			
		||||
	if w.Daily {
 | 
			
		||||
		go w.dailyRotate(w.dailyOpenTime)
 | 
			
		||||
	}
 | 
			
		||||
	if fInfo.Size() > 0 {
 | 
			
		||||
		count, err := w.lines()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		w.maxLinesCurLines = count
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *fileLogWriter) dailyRotate(openTime time.Time) {
 | 
			
		||||
	y, m, d := openTime.Add(24 * time.Hour).Date()
 | 
			
		||||
	nextDay := time.Date(y, m, d, 0, 0, 0, 0, openTime.Location())
 | 
			
		||||
	tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100))
 | 
			
		||||
	select {
 | 
			
		||||
	case <-tm.C:
 | 
			
		||||
		w.Lock()
 | 
			
		||||
		if w.needRotate(0, time.Now().Day()) {
 | 
			
		||||
			if err := w.doRotate(time.Now()); err != nil {
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		w.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *fileLogWriter) lines() (int, error) {
 | 
			
		||||
	fd, err := os.Open(w.Filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	defer fd.Close()
 | 
			
		||||
 | 
			
		||||
	buf := make([]byte, 32768) // 32k
 | 
			
		||||
	count := 0
 | 
			
		||||
	lineSep := []byte{'\n'}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		c, err := fd.Read(buf)
 | 
			
		||||
		if err != nil && err != io.EOF {
 | 
			
		||||
			return count, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		count += bytes.Count(buf[:c], lineSep)
 | 
			
		||||
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return count, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DoRotate means it need to write file in new file.
 | 
			
		||||
// new file name like xx.2013-01-01.log (daily) or xx.001.log (by line or size)
 | 
			
		||||
func (w *fileLogWriter) doRotate(logTime time.Time) error {
 | 
			
		||||
	// file exists
 | 
			
		||||
	// Find the next available number
 | 
			
		||||
	num := 1
 | 
			
		||||
	fName := ""
 | 
			
		||||
 | 
			
		||||
	_, err := os.Lstat(w.Filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		//even if the file is not exist or other ,we should RESTART the logger
 | 
			
		||||
		goto RESTART_LOGGER
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if w.MaxLines > 0 || w.MaxSize > 0 {
 | 
			
		||||
		for ; err == nil && num <= 999; num++ {
 | 
			
		||||
			fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, w.suffix)
 | 
			
		||||
			_, err = os.Lstat(fName)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		fName = fmt.Sprintf("%s.%s%s", w.fileNameOnly, w.dailyOpenTime.Format("2006-01-02"), w.suffix)
 | 
			
		||||
		_, err = os.Lstat(fName)
 | 
			
		||||
		for ; err == nil && num <= 999; num++ {
 | 
			
		||||
			fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", w.dailyOpenTime.Format("2006-01-02"), num, w.suffix)
 | 
			
		||||
			_, err = os.Lstat(fName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// return error if the last file checked still existed
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// close fileWriter before rename
 | 
			
		||||
	w.fileWriter.Close()
 | 
			
		||||
 | 
			
		||||
	// Rename the file to its new found name
 | 
			
		||||
	// even if occurs error,we MUST guarantee to  restart new logger
 | 
			
		||||
	err = os.Rename(w.Filename, fName)
 | 
			
		||||
	err = os.Chmod(fName, os.FileMode(0440))
 | 
			
		||||
	// re-start logger
 | 
			
		||||
RESTART_LOGGER:
 | 
			
		||||
 | 
			
		||||
	startLoggerErr := w.startLogger()
 | 
			
		||||
	go w.deleteOldLog()
 | 
			
		||||
 | 
			
		||||
	if startLoggerErr != nil {
 | 
			
		||||
		return fmt.Errorf("Rotate StartLogger: %s\n", startLoggerErr)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Rotate: %s\n", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *fileLogWriter) deleteOldLog() {
 | 
			
		||||
	dir := filepath.Dir(w.Filename)
 | 
			
		||||
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if r := recover(); r != nil {
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "Unable to delete old log '%s', error: %v\n", path, r)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if info == nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !info.IsDir() && info.ModTime().Add(24*time.Hour*time.Duration(w.MaxDays)).Before(time.Now()) {
 | 
			
		||||
			if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
 | 
			
		||||
				strings.HasSuffix(filepath.Base(path), w.suffix) {
 | 
			
		||||
				os.Remove(path)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy close the file description, close file writer.
 | 
			
		||||
func (w *fileLogWriter) Destroy() {
 | 
			
		||||
	w.fileWriter.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flush flush file logger.
 | 
			
		||||
// there are no buffering messages in file logger in memory.
 | 
			
		||||
// flush file means sync file from disk.
 | 
			
		||||
func (w *fileLogWriter) Flush() {
 | 
			
		||||
	w.fileWriter.Sync()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register(AdapterFile, newFileWriter)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								vendor/github.com/fatedier/beego/logs/jianliao.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/fatedier/beego/logs/jianliao.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,78 +0,0 @@
 | 
			
		||||
package logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// JLWriter implements beego LoggerInterface and is used to send jiaoliao webhook
 | 
			
		||||
type JLWriter struct {
 | 
			
		||||
	AuthorName  string `json:"authorname"`
 | 
			
		||||
	Title       string `json:"title"`
 | 
			
		||||
	WebhookURL  string `json:"webhookurl"`
 | 
			
		||||
	RedirectURL string `json:"redirecturl,omitempty"`
 | 
			
		||||
	ImageURL    string `json:"imageurl,omitempty"`
 | 
			
		||||
	Level       int    `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newJLWriter create jiaoliao writer.
 | 
			
		||||
func newJLWriter() Logger {
 | 
			
		||||
	return &JLWriter{Level: LevelTrace}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init JLWriter with json config string
 | 
			
		||||
func (s *JLWriter) Init(jsonconfig string) error {
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonconfig), s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMsg write message in smtp writer.
 | 
			
		||||
// it will send an email with subject and only this message.
 | 
			
		||||
func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error {
 | 
			
		||||
	if level > s.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text := fmt.Sprintf("%s %s", when.Format("2006-01-02 15:04:05"), msg)
 | 
			
		||||
 | 
			
		||||
	form := url.Values{}
 | 
			
		||||
	form.Add("authorName", s.AuthorName)
 | 
			
		||||
	form.Add("title", s.Title)
 | 
			
		||||
	form.Add("text", text)
 | 
			
		||||
	if s.RedirectURL != "" {
 | 
			
		||||
		form.Add("redirectUrl", s.RedirectURL)
 | 
			
		||||
	}
 | 
			
		||||
	if s.ImageURL != "" {
 | 
			
		||||
		form.Add("imageUrl", s.ImageURL)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := http.PostForm(s.WebhookURL, form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flush implementing method. empty.
 | 
			
		||||
func (s *JLWriter) Flush() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy implementing method. empty.
 | 
			
		||||
func (s *JLWriter) Destroy() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register(AdapterJianLiao, newJLWriter)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										657
									
								
								vendor/github.com/fatedier/beego/logs/log.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										657
									
								
								vendor/github.com/fatedier/beego/logs/log.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,657 +0,0 @@
 | 
			
		||||
// Copyright 2012 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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 logs provide a general log interface
 | 
			
		||||
// Usage:
 | 
			
		||||
//
 | 
			
		||||
// import "github.com/astaxie/beego/logs"
 | 
			
		||||
//
 | 
			
		||||
//	log := NewLogger(10000)
 | 
			
		||||
//	log.SetLogger("console", "")
 | 
			
		||||
//
 | 
			
		||||
//	> the first params stand for how many channel
 | 
			
		||||
//
 | 
			
		||||
// Use it like this:
 | 
			
		||||
//
 | 
			
		||||
//	log.Trace("trace")
 | 
			
		||||
//	log.Info("info")
 | 
			
		||||
//	log.Warn("warning")
 | 
			
		||||
//	log.Debug("debug")
 | 
			
		||||
//	log.Critical("critical")
 | 
			
		||||
//
 | 
			
		||||
//  more docs http://beego.me/docs/module/logs.md
 | 
			
		||||
package logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RFC5424 log message levels.
 | 
			
		||||
const (
 | 
			
		||||
	LevelEmergency = iota
 | 
			
		||||
	LevelAlert
 | 
			
		||||
	LevelCritical
 | 
			
		||||
	LevelError
 | 
			
		||||
	LevelWarning
 | 
			
		||||
	LevelNotice
 | 
			
		||||
	LevelInformational
 | 
			
		||||
	LevelDebug
 | 
			
		||||
	LevelTrace
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// levelLogLogger is defined to implement log.Logger
 | 
			
		||||
// the real log level will be LevelEmergency
 | 
			
		||||
const levelLoggerImpl = -1
 | 
			
		||||
 | 
			
		||||
// Name for adapter with beego official support
 | 
			
		||||
const (
 | 
			
		||||
	AdapterConsole   = "console"
 | 
			
		||||
	AdapterFile      = "file"
 | 
			
		||||
	AdapterMultiFile = "multifile"
 | 
			
		||||
	AdapterMail      = "smtp"
 | 
			
		||||
	AdapterConn      = "conn"
 | 
			
		||||
	AdapterEs        = "es"
 | 
			
		||||
	AdapterJianLiao  = "jianliao"
 | 
			
		||||
	AdapterSlack     = "slack"
 | 
			
		||||
	AdapterAliLS     = "alils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Legacy log level constants to ensure backwards compatibility.
 | 
			
		||||
const (
 | 
			
		||||
	LevelInfo = LevelInformational
 | 
			
		||||
	LevelWarn = LevelWarning
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type newLoggerFunc func() Logger
 | 
			
		||||
 | 
			
		||||
// Logger defines the behavior of a log provider.
 | 
			
		||||
type Logger interface {
 | 
			
		||||
	Init(config string) error
 | 
			
		||||
	WriteMsg(when time.Time, msg string, level int) error
 | 
			
		||||
	Destroy()
 | 
			
		||||
	Flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var adapters = make(map[string]newLoggerFunc)
 | 
			
		||||
var levelPrefix = [LevelTrace + 1]string{"[M] ", "[A] ", "[C] ", "[E] ", "[W] ", "[N] ", "[I] ", "[D] ", "[T] "}
 | 
			
		||||
 | 
			
		||||
// Register makes a log provide available by the provided name.
 | 
			
		||||
// If Register is called twice with the same name or if driver is nil,
 | 
			
		||||
// it panics.
 | 
			
		||||
func Register(name string, log newLoggerFunc) {
 | 
			
		||||
	if log == nil {
 | 
			
		||||
		panic("logs: Register provide is nil")
 | 
			
		||||
	}
 | 
			
		||||
	if _, dup := adapters[name]; dup {
 | 
			
		||||
		panic("logs: Register called twice for provider " + name)
 | 
			
		||||
	}
 | 
			
		||||
	adapters[name] = log
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BeeLogger is default logger in beego application.
 | 
			
		||||
// it can contain several providers and log message into all providers.
 | 
			
		||||
type BeeLogger struct {
 | 
			
		||||
	lock                sync.Mutex
 | 
			
		||||
	level               int
 | 
			
		||||
	init                bool
 | 
			
		||||
	enableFuncCallDepth bool
 | 
			
		||||
	loggerFuncCallDepth int
 | 
			
		||||
	asynchronous        bool
 | 
			
		||||
	msgChanLen          int64
 | 
			
		||||
	msgChan             chan *logMsg
 | 
			
		||||
	signalChan          chan string
 | 
			
		||||
	wg                  sync.WaitGroup
 | 
			
		||||
	outputs             []*nameLogger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const defaultAsyncMsgLen = 1e3
 | 
			
		||||
 | 
			
		||||
type nameLogger struct {
 | 
			
		||||
	Logger
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type logMsg struct {
 | 
			
		||||
	level int
 | 
			
		||||
	msg   string
 | 
			
		||||
	when  time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var logMsgPool *sync.Pool
 | 
			
		||||
 | 
			
		||||
// NewLogger returns a new BeeLogger.
 | 
			
		||||
// channelLen means the number of messages in chan(used where asynchronous is true).
 | 
			
		||||
// if the buffering chan is full, logger adapters write to file or other way.
 | 
			
		||||
func NewLogger(channelLens ...int64) *BeeLogger {
 | 
			
		||||
	bl := new(BeeLogger)
 | 
			
		||||
	bl.level = LevelDebug
 | 
			
		||||
	bl.loggerFuncCallDepth = 2
 | 
			
		||||
	bl.msgChanLen = append(channelLens, 0)[0]
 | 
			
		||||
	if bl.msgChanLen <= 0 {
 | 
			
		||||
		bl.msgChanLen = defaultAsyncMsgLen
 | 
			
		||||
	}
 | 
			
		||||
	bl.signalChan = make(chan string, 1)
 | 
			
		||||
	bl.setLogger(AdapterConsole)
 | 
			
		||||
	return bl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Async set the log to asynchronous and start the goroutine
 | 
			
		||||
func (bl *BeeLogger) Async(msgLen ...int64) *BeeLogger {
 | 
			
		||||
	bl.lock.Lock()
 | 
			
		||||
	defer bl.lock.Unlock()
 | 
			
		||||
	if bl.asynchronous {
 | 
			
		||||
		return bl
 | 
			
		||||
	}
 | 
			
		||||
	bl.asynchronous = true
 | 
			
		||||
	if len(msgLen) > 0 && msgLen[0] > 0 {
 | 
			
		||||
		bl.msgChanLen = msgLen[0]
 | 
			
		||||
	}
 | 
			
		||||
	bl.msgChan = make(chan *logMsg, bl.msgChanLen)
 | 
			
		||||
	logMsgPool = &sync.Pool{
 | 
			
		||||
		New: func() interface{} {
 | 
			
		||||
			return &logMsg{}
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	bl.wg.Add(1)
 | 
			
		||||
	go bl.startLogger()
 | 
			
		||||
	return bl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogger provides a given logger adapter into BeeLogger with config string.
 | 
			
		||||
// config need to be correct JSON as string: {"interval":360}.
 | 
			
		||||
func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error {
 | 
			
		||||
	config := append(configs, "{}")[0]
 | 
			
		||||
	for _, l := range bl.outputs {
 | 
			
		||||
		if l.name == adapterName {
 | 
			
		||||
			return fmt.Errorf("logs: duplicate adaptername %q (you have set this logger before)", adapterName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log, ok := adapters[adapterName]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lg := log()
 | 
			
		||||
	err := lg.Init(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintln(os.Stderr, "logs.BeeLogger.SetLogger: "+err.Error())
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	bl.outputs = append(bl.outputs, &nameLogger{name: adapterName, Logger: lg})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogger provides a given logger adapter into BeeLogger with config string.
 | 
			
		||||
// config need to be correct JSON as string: {"interval":360}.
 | 
			
		||||
func (bl *BeeLogger) SetLogger(adapterName string, configs ...string) error {
 | 
			
		||||
	bl.lock.Lock()
 | 
			
		||||
	defer bl.lock.Unlock()
 | 
			
		||||
	if !bl.init {
 | 
			
		||||
		bl.outputs = []*nameLogger{}
 | 
			
		||||
		bl.init = true
 | 
			
		||||
	}
 | 
			
		||||
	return bl.setLogger(adapterName, configs...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DelLogger remove a logger adapter in BeeLogger.
 | 
			
		||||
func (bl *BeeLogger) DelLogger(adapterName string) error {
 | 
			
		||||
	bl.lock.Lock()
 | 
			
		||||
	defer bl.lock.Unlock()
 | 
			
		||||
	outputs := []*nameLogger{}
 | 
			
		||||
	for _, lg := range bl.outputs {
 | 
			
		||||
		if lg.name == adapterName {
 | 
			
		||||
			lg.Destroy()
 | 
			
		||||
		} else {
 | 
			
		||||
			outputs = append(outputs, lg)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(outputs) == len(bl.outputs) {
 | 
			
		||||
		return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName)
 | 
			
		||||
	}
 | 
			
		||||
	bl.outputs = outputs
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bl *BeeLogger) writeToLoggers(when time.Time, msg string, level int) {
 | 
			
		||||
	for _, l := range bl.outputs {
 | 
			
		||||
		err := l.WriteMsg(when, msg, level)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bl *BeeLogger) Write(p []byte) (n int, err error) {
 | 
			
		||||
	if len(p) == 0 {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	// writeMsg will always add a '\n' character
 | 
			
		||||
	if p[len(p)-1] == '\n' {
 | 
			
		||||
		p = p[0 : len(p)-1]
 | 
			
		||||
	}
 | 
			
		||||
	// set levelLoggerImpl to ensure all log message will be write out
 | 
			
		||||
	err = bl.writeMsg(levelLoggerImpl, string(p))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return len(p), err
 | 
			
		||||
	}
 | 
			
		||||
	return 0, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error {
 | 
			
		||||
	if !bl.init {
 | 
			
		||||
		bl.lock.Lock()
 | 
			
		||||
		bl.setLogger(AdapterConsole)
 | 
			
		||||
		bl.lock.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(v) > 0 {
 | 
			
		||||
		msg = fmt.Sprintf(msg, v...)
 | 
			
		||||
	}
 | 
			
		||||
	when := time.Now()
 | 
			
		||||
	if bl.enableFuncCallDepth {
 | 
			
		||||
		_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			file = "???"
 | 
			
		||||
			line = 0
 | 
			
		||||
		} else {
 | 
			
		||||
			if strings.Contains(file, "<autogenerated>") {
 | 
			
		||||
				_, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					file = "???"
 | 
			
		||||
					line = 0
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		_, filename := path.Split(file)
 | 
			
		||||
		msg = "[" + filename + ":" + strconv.FormatInt(int64(line), 10) + "] " + msg
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//set level info in front of filename info
 | 
			
		||||
	if logLevel == levelLoggerImpl {
 | 
			
		||||
		// set to emergency to ensure all log will be print out correctly
 | 
			
		||||
		logLevel = LevelEmergency
 | 
			
		||||
	} else {
 | 
			
		||||
		msg = levelPrefix[logLevel] + msg
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bl.asynchronous {
 | 
			
		||||
		lm := logMsgPool.Get().(*logMsg)
 | 
			
		||||
		lm.level = logLevel
 | 
			
		||||
		lm.msg = msg
 | 
			
		||||
		lm.when = when
 | 
			
		||||
		bl.msgChan <- lm
 | 
			
		||||
	} else {
 | 
			
		||||
		bl.writeToLoggers(when, msg, logLevel)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLevel Set log message level.
 | 
			
		||||
// If message level (such as LevelDebug) is higher than logger level (such as LevelWarning),
 | 
			
		||||
// log providers will not even be sent the message.
 | 
			
		||||
func (bl *BeeLogger) SetLevel(l int) {
 | 
			
		||||
	bl.level = l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogFuncCallDepth set log funcCallDepth
 | 
			
		||||
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
 | 
			
		||||
	bl.loggerFuncCallDepth = d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLogFuncCallDepth return log funcCallDepth for wrapper
 | 
			
		||||
func (bl *BeeLogger) GetLogFuncCallDepth() int {
 | 
			
		||||
	return bl.loggerFuncCallDepth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnableFuncCallDepth enable log funcCallDepth
 | 
			
		||||
func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
 | 
			
		||||
	bl.enableFuncCallDepth = b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// start logger chan reading.
 | 
			
		||||
// when chan is not empty, write logs.
 | 
			
		||||
func (bl *BeeLogger) startLogger() {
 | 
			
		||||
	gameOver := false
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case bm := <-bl.msgChan:
 | 
			
		||||
			bl.writeToLoggers(bm.when, bm.msg, bm.level)
 | 
			
		||||
			logMsgPool.Put(bm)
 | 
			
		||||
		case sg := <-bl.signalChan:
 | 
			
		||||
			// Now should only send "flush" or "close" to bl.signalChan
 | 
			
		||||
			bl.flush()
 | 
			
		||||
			if sg == "close" {
 | 
			
		||||
				for _, l := range bl.outputs {
 | 
			
		||||
					l.Destroy()
 | 
			
		||||
				}
 | 
			
		||||
				bl.outputs = nil
 | 
			
		||||
				gameOver = true
 | 
			
		||||
			}
 | 
			
		||||
			bl.wg.Done()
 | 
			
		||||
		}
 | 
			
		||||
		if gameOver {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Emergency Log EMERGENCY level message.
 | 
			
		||||
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
 | 
			
		||||
	if LevelEmergency > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelEmergency, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alert Log ALERT level message.
 | 
			
		||||
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
 | 
			
		||||
	if LevelAlert > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelAlert, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Critical Log CRITICAL level message.
 | 
			
		||||
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
 | 
			
		||||
	if LevelCritical > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelCritical, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error Log ERROR level message.
 | 
			
		||||
func (bl *BeeLogger) Error(format string, v ...interface{}) {
 | 
			
		||||
	if LevelError > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelError, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warning Log WARNING level message.
 | 
			
		||||
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
 | 
			
		||||
	if LevelWarn > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelWarn, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Notice Log NOTICE level message.
 | 
			
		||||
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
 | 
			
		||||
	if LevelNotice > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelNotice, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Informational Log INFORMATIONAL level message.
 | 
			
		||||
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
 | 
			
		||||
	if LevelInfo > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelInfo, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug Log DEBUG level message.
 | 
			
		||||
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
 | 
			
		||||
	if LevelDebug > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelDebug, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warn Log WARN level message.
 | 
			
		||||
// compatibility alias for Warning()
 | 
			
		||||
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
 | 
			
		||||
	if LevelWarn > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelWarn, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info Log INFO level message.
 | 
			
		||||
// compatibility alias for Informational()
 | 
			
		||||
func (bl *BeeLogger) Info(format string, v ...interface{}) {
 | 
			
		||||
	if LevelInfo > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelInfo, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Trace Log TRACE level message.
 | 
			
		||||
// compatibility alias for Debug()
 | 
			
		||||
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
 | 
			
		||||
	if LevelTrace > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.writeMsg(LevelTrace, format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flush flush all chan data.
 | 
			
		||||
func (bl *BeeLogger) Flush() {
 | 
			
		||||
	if bl.asynchronous {
 | 
			
		||||
		bl.signalChan <- "flush"
 | 
			
		||||
		bl.wg.Wait()
 | 
			
		||||
		bl.wg.Add(1)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bl.flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close close logger, flush all chan data and destroy all adapters in BeeLogger.
 | 
			
		||||
func (bl *BeeLogger) Close() {
 | 
			
		||||
	if bl.asynchronous {
 | 
			
		||||
		bl.signalChan <- "close"
 | 
			
		||||
		bl.wg.Wait()
 | 
			
		||||
		close(bl.msgChan)
 | 
			
		||||
	} else {
 | 
			
		||||
		bl.flush()
 | 
			
		||||
		for _, l := range bl.outputs {
 | 
			
		||||
			l.Destroy()
 | 
			
		||||
		}
 | 
			
		||||
		bl.outputs = nil
 | 
			
		||||
	}
 | 
			
		||||
	close(bl.signalChan)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset close all outputs, and set bl.outputs to nil
 | 
			
		||||
func (bl *BeeLogger) Reset() {
 | 
			
		||||
	bl.Flush()
 | 
			
		||||
	for _, l := range bl.outputs {
 | 
			
		||||
		l.Destroy()
 | 
			
		||||
	}
 | 
			
		||||
	bl.outputs = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bl *BeeLogger) flush() {
 | 
			
		||||
	if bl.asynchronous {
 | 
			
		||||
		for {
 | 
			
		||||
			if len(bl.msgChan) > 0 {
 | 
			
		||||
				bm := <-bl.msgChan
 | 
			
		||||
				bl.writeToLoggers(bm.when, bm.msg, bm.level)
 | 
			
		||||
				logMsgPool.Put(bm)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, l := range bl.outputs {
 | 
			
		||||
		l.Flush()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// beeLogger references the used application logger.
 | 
			
		||||
var beeLogger *BeeLogger = NewLogger()
 | 
			
		||||
 | 
			
		||||
// GetLogger returns the default BeeLogger
 | 
			
		||||
func GetBeeLogger() *BeeLogger {
 | 
			
		||||
	return beeLogger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var beeLoggerMap = struct {
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	logs map[string]*log.Logger
 | 
			
		||||
}{
 | 
			
		||||
	logs: map[string]*log.Logger{},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLogger returns the default BeeLogger
 | 
			
		||||
func GetLogger(prefixes ...string) *log.Logger {
 | 
			
		||||
	prefix := append(prefixes, "")[0]
 | 
			
		||||
	if prefix != "" {
 | 
			
		||||
		prefix = fmt.Sprintf(`[%s] `, strings.ToUpper(prefix))
 | 
			
		||||
	}
 | 
			
		||||
	beeLoggerMap.RLock()
 | 
			
		||||
	l, ok := beeLoggerMap.logs[prefix]
 | 
			
		||||
	if ok {
 | 
			
		||||
		beeLoggerMap.RUnlock()
 | 
			
		||||
		return l
 | 
			
		||||
	}
 | 
			
		||||
	beeLoggerMap.RUnlock()
 | 
			
		||||
	beeLoggerMap.Lock()
 | 
			
		||||
	defer beeLoggerMap.Unlock()
 | 
			
		||||
	l, ok = beeLoggerMap.logs[prefix]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		l = log.New(beeLogger, prefix, 0)
 | 
			
		||||
		beeLoggerMap.logs[prefix] = l
 | 
			
		||||
	}
 | 
			
		||||
	return l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset will remove all the adapter
 | 
			
		||||
func Reset() {
 | 
			
		||||
	beeLogger.Reset()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Async(msgLen ...int64) *BeeLogger {
 | 
			
		||||
	return beeLogger.Async(msgLen...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLevel sets the global log level used by the simple logger.
 | 
			
		||||
func SetLevel(l int) {
 | 
			
		||||
	beeLogger.SetLevel(l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnableFuncCallDepth enable log funcCallDepth
 | 
			
		||||
func EnableFuncCallDepth(b bool) {
 | 
			
		||||
	beeLogger.enableFuncCallDepth = b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogFuncCall set the CallDepth, default is 4
 | 
			
		||||
func SetLogFuncCall(b bool) {
 | 
			
		||||
	beeLogger.EnableFuncCallDepth(b)
 | 
			
		||||
	beeLogger.SetLogFuncCallDepth(4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogFuncCallDepth set log funcCallDepth
 | 
			
		||||
func SetLogFuncCallDepth(d int) {
 | 
			
		||||
	beeLogger.loggerFuncCallDepth = d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogger sets a new logger.
 | 
			
		||||
func SetLogger(adapter string, config ...string) error {
 | 
			
		||||
	err := beeLogger.SetLogger(adapter, config...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Emergency logs a message at emergency level.
 | 
			
		||||
func Emergency(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Emergency(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alert logs a message at alert level.
 | 
			
		||||
func Alert(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Alert(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Critical logs a message at critical level.
 | 
			
		||||
func Critical(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Critical(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error logs a message at error level.
 | 
			
		||||
func Error(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Error(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warning logs a message at warning level.
 | 
			
		||||
func Warning(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Warn(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warn compatibility alias for Warning()
 | 
			
		||||
func Warn(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Warn(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Notice logs a message at notice level.
 | 
			
		||||
func Notice(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Notice(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Informational logs a message at info level.
 | 
			
		||||
func Informational(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Info(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info compatibility alias for Warning()
 | 
			
		||||
func Info(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Info(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug logs a message at debug level.
 | 
			
		||||
func Debug(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Debug(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Trace logs a message at trace level.
 | 
			
		||||
// compatibility alias for Warning()
 | 
			
		||||
func Trace(f interface{}, v ...interface{}) {
 | 
			
		||||
	beeLogger.Trace(formatLog(f, v...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func formatLog(f interface{}, v ...interface{}) string {
 | 
			
		||||
	var msg string
 | 
			
		||||
	switch f.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		msg = f.(string)
 | 
			
		||||
		if len(v) == 0 {
 | 
			
		||||
			return msg
 | 
			
		||||
		}
 | 
			
		||||
		if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") {
 | 
			
		||||
			//format string
 | 
			
		||||
		} else {
 | 
			
		||||
			//do not contain format char
 | 
			
		||||
			msg += strings.Repeat(" %v", len(v))
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		msg = fmt.Sprint(f)
 | 
			
		||||
		if len(v) == 0 {
 | 
			
		||||
			return msg
 | 
			
		||||
		}
 | 
			
		||||
		msg += strings.Repeat(" %v", len(v))
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf(msg, v...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										188
									
								
								vendor/github.com/fatedier/beego/logs/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										188
									
								
								vendor/github.com/fatedier/beego/logs/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,188 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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 logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type logWriter struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	writer io.Writer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newLogWriter(wr io.Writer) *logWriter {
 | 
			
		||||
	return &logWriter{writer: wr}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lg *logWriter) println(when time.Time, msg string) {
 | 
			
		||||
	lg.Lock()
 | 
			
		||||
	h, _ := formatTimeHeader(when)
 | 
			
		||||
	lg.writer.Write(append(append(h, msg...), '\n'))
 | 
			
		||||
	lg.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type outputMode int
 | 
			
		||||
 | 
			
		||||
// DiscardNonColorEscSeq supports the divided color escape sequence.
 | 
			
		||||
// But non-color escape sequence is not output.
 | 
			
		||||
// Please use the OutputNonColorEscSeq If you want to output a non-color
 | 
			
		||||
// escape sequences such as ncurses. However, it does not support the divided
 | 
			
		||||
// color escape sequence.
 | 
			
		||||
const (
 | 
			
		||||
	_ outputMode = iota
 | 
			
		||||
	DiscardNonColorEscSeq
 | 
			
		||||
	OutputNonColorEscSeq
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewAnsiColorWriter creates and initializes a new ansiColorWriter
 | 
			
		||||
// using io.Writer w as its initial contents.
 | 
			
		||||
// In the console of Windows, which change the foreground and background
 | 
			
		||||
// colors of the text by the escape sequence.
 | 
			
		||||
// In the console of other systems, which writes to w all text.
 | 
			
		||||
func NewAnsiColorWriter(w io.Writer) io.Writer {
 | 
			
		||||
	return NewModeAnsiColorWriter(w, DiscardNonColorEscSeq)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewModeAnsiColorWriter create and initializes a new ansiColorWriter
 | 
			
		||||
// by specifying the outputMode.
 | 
			
		||||
func NewModeAnsiColorWriter(w io.Writer, mode outputMode) io.Writer {
 | 
			
		||||
	if _, ok := w.(*ansiColorWriter); !ok {
 | 
			
		||||
		return &ansiColorWriter{
 | 
			
		||||
			w:    w,
 | 
			
		||||
			mode: mode,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return w
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	y1  = `0123456789`
 | 
			
		||||
	y2  = `0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789`
 | 
			
		||||
	y3  = `0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999`
 | 
			
		||||
	y4  = `0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789`
 | 
			
		||||
	mo1 = `000000000111`
 | 
			
		||||
	mo2 = `123456789012`
 | 
			
		||||
	d1  = `0000000001111111111222222222233`
 | 
			
		||||
	d2  = `1234567890123456789012345678901`
 | 
			
		||||
	h1  = `000000000011111111112222`
 | 
			
		||||
	h2  = `012345678901234567890123`
 | 
			
		||||
	mi1 = `000000000011111111112222222222333333333344444444445555555555`
 | 
			
		||||
	mi2 = `012345678901234567890123456789012345678901234567890123456789`
 | 
			
		||||
	s1  = `000000000011111111112222222222333333333344444444445555555555`
 | 
			
		||||
	s2  = `012345678901234567890123456789012345678901234567890123456789`
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func formatTimeHeader(when time.Time) ([]byte, int) {
 | 
			
		||||
	y, mo, d := when.Date()
 | 
			
		||||
	h, mi, s := when.Clock()
 | 
			
		||||
	//len("2006/01/02 15:04:05 ")==20
 | 
			
		||||
	var buf [20]byte
 | 
			
		||||
 | 
			
		||||
	buf[0] = y1[y/1000%10]
 | 
			
		||||
	buf[1] = y2[y/100]
 | 
			
		||||
	buf[2] = y3[y-y/100*100]
 | 
			
		||||
	buf[3] = y4[y-y/100*100]
 | 
			
		||||
	buf[4] = '/'
 | 
			
		||||
	buf[5] = mo1[mo-1]
 | 
			
		||||
	buf[6] = mo2[mo-1]
 | 
			
		||||
	buf[7] = '/'
 | 
			
		||||
	buf[8] = d1[d-1]
 | 
			
		||||
	buf[9] = d2[d-1]
 | 
			
		||||
	buf[10] = ' '
 | 
			
		||||
	buf[11] = h1[h]
 | 
			
		||||
	buf[12] = h2[h]
 | 
			
		||||
	buf[13] = ':'
 | 
			
		||||
	buf[14] = mi1[mi]
 | 
			
		||||
	buf[15] = mi2[mi]
 | 
			
		||||
	buf[16] = ':'
 | 
			
		||||
	buf[17] = s1[s]
 | 
			
		||||
	buf[18] = s2[s]
 | 
			
		||||
	buf[19] = ' '
 | 
			
		||||
 | 
			
		||||
	return buf[0:], d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	green   = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
 | 
			
		||||
	white   = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
 | 
			
		||||
	yellow  = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
 | 
			
		||||
	red     = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
 | 
			
		||||
	blue    = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
 | 
			
		||||
	magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
 | 
			
		||||
	cyan    = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
 | 
			
		||||
 | 
			
		||||
	w32Green   = string([]byte{27, 91, 52, 50, 109})
 | 
			
		||||
	w32White   = string([]byte{27, 91, 52, 55, 109})
 | 
			
		||||
	w32Yellow  = string([]byte{27, 91, 52, 51, 109})
 | 
			
		||||
	w32Red     = string([]byte{27, 91, 52, 49, 109})
 | 
			
		||||
	w32Blue    = string([]byte{27, 91, 52, 52, 109})
 | 
			
		||||
	w32Magenta = string([]byte{27, 91, 52, 53, 109})
 | 
			
		||||
	w32Cyan    = string([]byte{27, 91, 52, 54, 109})
 | 
			
		||||
 | 
			
		||||
	reset = string([]byte{27, 91, 48, 109})
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ColorByStatus(cond bool, code int) string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case code >= 200 && code < 300:
 | 
			
		||||
		return map[bool]string{true: green, false: w32Green}[cond]
 | 
			
		||||
	case code >= 300 && code < 400:
 | 
			
		||||
		return map[bool]string{true: white, false: w32White}[cond]
 | 
			
		||||
	case code >= 400 && code < 500:
 | 
			
		||||
		return map[bool]string{true: yellow, false: w32Yellow}[cond]
 | 
			
		||||
	default:
 | 
			
		||||
		return map[bool]string{true: red, false: w32Red}[cond]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ColorByMethod(cond bool, method string) string {
 | 
			
		||||
	switch method {
 | 
			
		||||
	case "GET":
 | 
			
		||||
		return map[bool]string{true: blue, false: w32Blue}[cond]
 | 
			
		||||
	case "POST":
 | 
			
		||||
		return map[bool]string{true: cyan, false: w32Cyan}[cond]
 | 
			
		||||
	case "PUT":
 | 
			
		||||
		return map[bool]string{true: yellow, false: w32Yellow}[cond]
 | 
			
		||||
	case "DELETE":
 | 
			
		||||
		return map[bool]string{true: red, false: w32Red}[cond]
 | 
			
		||||
	case "PATCH":
 | 
			
		||||
		return map[bool]string{true: green, false: w32Green}[cond]
 | 
			
		||||
	case "HEAD":
 | 
			
		||||
		return map[bool]string{true: magenta, false: w32Magenta}[cond]
 | 
			
		||||
	case "OPTIONS":
 | 
			
		||||
		return map[bool]string{true: white, false: w32White}[cond]
 | 
			
		||||
	default:
 | 
			
		||||
		return reset
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Guard Mutex to guarantee atomicity of W32Debug(string) function
 | 
			
		||||
var mu sync.Mutex
 | 
			
		||||
 | 
			
		||||
// Helper method to output colored logs in Windows terminals
 | 
			
		||||
func W32Debug(msg string) {
 | 
			
		||||
	mu.Lock()
 | 
			
		||||
	defer mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	current := time.Now()
 | 
			
		||||
	w := NewAnsiColorWriter(os.Stdout)
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(w, "[beego] %v %s\n", current.Format("2006/01/02 - 15:04:05"), msg)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								vendor/github.com/fatedier/beego/logs/multifile.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/fatedier/beego/logs/multifile.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,116 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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 logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A filesLogWriter manages several fileLogWriter
 | 
			
		||||
// filesLogWriter will write logs to the file in json configuration  and write the same level log to correspond file
 | 
			
		||||
// means if the file name in configuration is project.log filesLogWriter will create project.error.log/project.debug.log
 | 
			
		||||
// and write the error-level logs to project.error.log and write the debug-level logs to project.debug.log
 | 
			
		||||
// the rotate attribute also  acts like fileLogWriter
 | 
			
		||||
type multiFileLogWriter struct {
 | 
			
		||||
	writers       [LevelDebug + 1 + 1]*fileLogWriter // the last one for fullLogWriter
 | 
			
		||||
	fullLogWriter *fileLogWriter
 | 
			
		||||
	Separate      []string `json:"separate"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var levelNames = [...]string{"emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"}
 | 
			
		||||
 | 
			
		||||
// Init file logger with json config.
 | 
			
		||||
// jsonConfig like:
 | 
			
		||||
//	{
 | 
			
		||||
//	"filename":"logs/beego.log",
 | 
			
		||||
//	"maxLines":0,
 | 
			
		||||
//	"maxsize":0,
 | 
			
		||||
//	"daily":true,
 | 
			
		||||
//	"maxDays":15,
 | 
			
		||||
//	"rotate":true,
 | 
			
		||||
//  	"perm":0600,
 | 
			
		||||
//	"separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"],
 | 
			
		||||
//	}
 | 
			
		||||
 | 
			
		||||
func (f *multiFileLogWriter) Init(config string) error {
 | 
			
		||||
	writer := newFileWriter().(*fileLogWriter)
 | 
			
		||||
	err := writer.Init(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	f.fullLogWriter = writer
 | 
			
		||||
	f.writers[LevelDebug+1] = writer
 | 
			
		||||
 | 
			
		||||
	//unmarshal "separate" field to f.Separate
 | 
			
		||||
	json.Unmarshal([]byte(config), f)
 | 
			
		||||
 | 
			
		||||
	jsonMap := map[string]interface{}{}
 | 
			
		||||
	json.Unmarshal([]byte(config), &jsonMap)
 | 
			
		||||
 | 
			
		||||
	for i := LevelEmergency; i < LevelDebug+1; i++ {
 | 
			
		||||
		for _, v := range f.Separate {
 | 
			
		||||
			if v == levelNames[i] {
 | 
			
		||||
				jsonMap["filename"] = f.fullLogWriter.fileNameOnly + "." + levelNames[i] + f.fullLogWriter.suffix
 | 
			
		||||
				jsonMap["level"] = i
 | 
			
		||||
				bs, _ := json.Marshal(jsonMap)
 | 
			
		||||
				writer = newFileWriter().(*fileLogWriter)
 | 
			
		||||
				writer.Init(string(bs))
 | 
			
		||||
				f.writers[i] = writer
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *multiFileLogWriter) Destroy() {
 | 
			
		||||
	for i := 0; i < len(f.writers); i++ {
 | 
			
		||||
		if f.writers[i] != nil {
 | 
			
		||||
			f.writers[i].Destroy()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *multiFileLogWriter) WriteMsg(when time.Time, msg string, level int) error {
 | 
			
		||||
	if f.fullLogWriter != nil {
 | 
			
		||||
		f.fullLogWriter.WriteMsg(when, msg, level)
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < len(f.writers)-1; i++ {
 | 
			
		||||
		if f.writers[i] != nil {
 | 
			
		||||
			if level == f.writers[i].Level {
 | 
			
		||||
				f.writers[i].WriteMsg(when, msg, level)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *multiFileLogWriter) Flush() {
 | 
			
		||||
	for i := 0; i < len(f.writers); i++ {
 | 
			
		||||
		if f.writers[i] != nil {
 | 
			
		||||
			f.writers[i].Flush()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newFilesWriter create a FileLogWriter returning as LoggerInterface.
 | 
			
		||||
func newFilesWriter() Logger {
 | 
			
		||||
	return &multiFileLogWriter{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register(AdapterMultiFile, newFilesWriter)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								vendor/github.com/fatedier/beego/logs/slack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/fatedier/beego/logs/slack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,66 +0,0 @@
 | 
			
		||||
package logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook
 | 
			
		||||
type SLACKWriter struct {
 | 
			
		||||
	WebhookURL string `json:"webhookurl"`
 | 
			
		||||
	Level      int    `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newSLACKWriter create jiaoliao writer.
 | 
			
		||||
func newSLACKWriter() Logger {
 | 
			
		||||
	return &SLACKWriter{Level: LevelTrace}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init SLACKWriter with json config string
 | 
			
		||||
func (s *SLACKWriter) Init(jsonconfig string) error {
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonconfig), s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMsg write message in smtp writer.
 | 
			
		||||
// it will send an email with subject and only this message.
 | 
			
		||||
func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error {
 | 
			
		||||
	if level > s.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text := fmt.Sprintf("{\"text\": \"%s %s\"}", when.Format("2006-01-02 15:04:05"), msg)
 | 
			
		||||
 | 
			
		||||
	form := url.Values{}
 | 
			
		||||
	form.Add("payload", text)
 | 
			
		||||
 | 
			
		||||
	resp, err := http.PostForm(s.WebhookURL, form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flush implementing method. empty.
 | 
			
		||||
func (s *SLACKWriter) Flush() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy implementing method. empty.
 | 
			
		||||
func (s *SLACKWriter) Destroy() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register(AdapterSlack, newSLACKWriter)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										160
									
								
								vendor/github.com/fatedier/beego/logs/smtp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										160
									
								
								vendor/github.com/fatedier/beego/logs/smtp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,160 +0,0 @@
 | 
			
		||||
// Copyright 2014 beego Author. All Rights Reserved.
 | 
			
		||||
//
 | 
			
		||||
// 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 logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/smtp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SMTPWriter implements LoggerInterface and is used to send emails via given SMTP-server.
 | 
			
		||||
type SMTPWriter struct {
 | 
			
		||||
	Username           string   `json:"username"`
 | 
			
		||||
	Password           string   `json:"password"`
 | 
			
		||||
	Host               string   `json:"host"`
 | 
			
		||||
	Subject            string   `json:"subject"`
 | 
			
		||||
	FromAddress        string   `json:"fromAddress"`
 | 
			
		||||
	RecipientAddresses []string `json:"sendTos"`
 | 
			
		||||
	Level              int      `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSMTPWriter create smtp writer.
 | 
			
		||||
func newSMTPWriter() Logger {
 | 
			
		||||
	return &SMTPWriter{Level: LevelTrace}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init smtp writer with json config.
 | 
			
		||||
// config like:
 | 
			
		||||
//	{
 | 
			
		||||
//		"username":"example@gmail.com",
 | 
			
		||||
//		"password:"password",
 | 
			
		||||
//		"host":"smtp.gmail.com:465",
 | 
			
		||||
//		"subject":"email title",
 | 
			
		||||
//		"fromAddress":"from@example.com",
 | 
			
		||||
//		"sendTos":["email1","email2"],
 | 
			
		||||
//		"level":LevelError
 | 
			
		||||
//	}
 | 
			
		||||
func (s *SMTPWriter) Init(jsonconfig string) error {
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonconfig), s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth {
 | 
			
		||||
	if len(strings.Trim(s.Username, " ")) == 0 && len(strings.Trim(s.Password, " ")) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return smtp.PlainAuth(
 | 
			
		||||
		"",
 | 
			
		||||
		s.Username,
 | 
			
		||||
		s.Password,
 | 
			
		||||
		host,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error {
 | 
			
		||||
	client, err := smtp.Dial(hostAddressWithPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host, _, _ := net.SplitHostPort(hostAddressWithPort)
 | 
			
		||||
	tlsConn := &tls.Config{
 | 
			
		||||
		InsecureSkipVerify: true,
 | 
			
		||||
		ServerName:         host,
 | 
			
		||||
	}
 | 
			
		||||
	if err = client.StartTLS(tlsConn); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if auth != nil {
 | 
			
		||||
		if err = client.Auth(auth); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = client.Mail(fromAddress); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, rec := range recipients {
 | 
			
		||||
		if err = client.Rcpt(rec); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w, err := client.Data()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = w.Write([]byte(msgContent))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = w.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = client.Quit()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMsg write message in smtp writer.
 | 
			
		||||
// it will send an email with subject and only this message.
 | 
			
		||||
func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error {
 | 
			
		||||
	if level > s.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hp := strings.Split(s.Host, ":")
 | 
			
		||||
 | 
			
		||||
	// Set up authentication information.
 | 
			
		||||
	auth := s.getSMTPAuth(hp[0])
 | 
			
		||||
 | 
			
		||||
	// Connect to the server, authenticate, set the sender and recipient,
 | 
			
		||||
	// and send the email all in one step.
 | 
			
		||||
	contentType := "Content-Type: text/plain" + "; charset=UTF-8"
 | 
			
		||||
	mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
 | 
			
		||||
		">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", when.Format("2006-01-02 15:04:05")) + msg)
 | 
			
		||||
 | 
			
		||||
	return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flush implementing method. empty.
 | 
			
		||||
func (s *SMTPWriter) Flush() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy implementing method. empty.
 | 
			
		||||
func (s *SMTPWriter) Destroy() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register(AdapterMail, newSMTPWriter)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										201
									
								
								vendor/github.com/fatedier/golib/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/fatedier/golib/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,201 +0,0 @@
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "{}"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright {yyyy} {name of copyright owner}
 | 
			
		||||
 | 
			
		||||
   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.
 | 
			
		||||
							
								
								
									
										62
									
								
								vendor/github.com/fatedier/golib/control/shutdown/shutdown.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/fatedier/golib/control/shutdown/shutdown.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,62 +0,0 @@
 | 
			
		||||
// Copyright 2017 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 shutdown
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Shutdown struct {
 | 
			
		||||
	doing   bool
 | 
			
		||||
	ending  bool
 | 
			
		||||
	startCh chan struct{}
 | 
			
		||||
	doneCh  chan struct{}
 | 
			
		||||
	mu      sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New() *Shutdown {
 | 
			
		||||
	return &Shutdown{
 | 
			
		||||
		doing:   false,
 | 
			
		||||
		ending:  false,
 | 
			
		||||
		startCh: make(chan struct{}),
 | 
			
		||||
		doneCh:  make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Shutdown) Start() {
 | 
			
		||||
	s.mu.Lock()
 | 
			
		||||
	defer s.mu.Unlock()
 | 
			
		||||
	if !s.doing {
 | 
			
		||||
		s.doing = true
 | 
			
		||||
		close(s.startCh)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Shutdown) WaitStart() {
 | 
			
		||||
	<-s.startCh
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Shutdown) Done() {
 | 
			
		||||
	s.mu.Lock()
 | 
			
		||||
	defer s.mu.Unlock()
 | 
			
		||||
	if !s.ending {
 | 
			
		||||
		s.ending = true
 | 
			
		||||
		close(s.doneCh)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Shutdown) WaitDone() {
 | 
			
		||||
	<-s.doneCh
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								vendor/github.com/fatedier/golib/crypto/decode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/fatedier/golib/crypto/decode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,75 +0,0 @@
 | 
			
		||||
// Copyright 2017 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 crypto
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/pbkdf2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewReader returns a new Reader that decrypts bytes from r
 | 
			
		||||
func NewReader(r io.Reader, key []byte) *Reader {
 | 
			
		||||
	key = pbkdf2.Key(key, []byte(DefaultSalt), 64, aes.BlockSize, sha1.New)
 | 
			
		||||
 | 
			
		||||
	return &Reader{
 | 
			
		||||
		r:   r,
 | 
			
		||||
		key: key,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reader is an io.Reader that can read encrypted bytes.
 | 
			
		||||
// Now it only supports aes-128-cfb.
 | 
			
		||||
type Reader struct {
 | 
			
		||||
	r   io.Reader
 | 
			
		||||
	dec *cipher.StreamReader
 | 
			
		||||
	key []byte
 | 
			
		||||
	iv  []byte
 | 
			
		||||
	err error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read satisfies the io.Reader interface.
 | 
			
		||||
func (r *Reader) Read(p []byte) (nRet int, errRet error) {
 | 
			
		||||
	if r.err != nil {
 | 
			
		||||
		return 0, r.err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.dec == nil {
 | 
			
		||||
		iv := make([]byte, aes.BlockSize)
 | 
			
		||||
		if _, errRet = io.ReadFull(r.r, iv); errRet != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		r.iv = iv
 | 
			
		||||
 | 
			
		||||
		block, err := aes.NewCipher(r.key)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			errRet = err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		r.dec = &cipher.StreamReader{
 | 
			
		||||
			S: cipher.NewCFBDecrypter(block, iv),
 | 
			
		||||
			R: r.r,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nRet, errRet = r.dec.Read(p)
 | 
			
		||||
	if errRet != nil {
 | 
			
		||||
		r.err = errRet
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								vendor/github.com/fatedier/golib/crypto/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										89
									
								
								vendor/github.com/fatedier/golib/crypto/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,89 +0,0 @@
 | 
			
		||||
// Copyright 2017 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 crypto
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/pbkdf2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	DefaultSalt = "crypto"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewWriter returns a new Writer that encrypts bytes to w.
 | 
			
		||||
func NewWriter(w io.Writer, key []byte) (*Writer, error) {
 | 
			
		||||
	key = pbkdf2.Key(key, []byte(DefaultSalt), 64, aes.BlockSize, sha1.New)
 | 
			
		||||
 | 
			
		||||
	// random iv
 | 
			
		||||
	iv := make([]byte, aes.BlockSize)
 | 
			
		||||
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block, err := aes.NewCipher(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Writer{
 | 
			
		||||
		w: w,
 | 
			
		||||
		enc: &cipher.StreamWriter{
 | 
			
		||||
			S: cipher.NewCFBEncrypter(block, iv),
 | 
			
		||||
			W: w,
 | 
			
		||||
		},
 | 
			
		||||
		key: key,
 | 
			
		||||
		iv:  iv,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Writer is an io.Writer that can write encrypted bytes.
 | 
			
		||||
// Now it only support aes-128-cfb.
 | 
			
		||||
type Writer struct {
 | 
			
		||||
	w      io.Writer
 | 
			
		||||
	enc    *cipher.StreamWriter
 | 
			
		||||
	key    []byte
 | 
			
		||||
	iv     []byte
 | 
			
		||||
	ivSend bool
 | 
			
		||||
	err    error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write satisfies the io.Writer interface.
 | 
			
		||||
func (w *Writer) Write(p []byte) (nRet int, errRet error) {
 | 
			
		||||
	if w.err != nil {
 | 
			
		||||
		return 0, w.err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// When write is first called, iv will be written to w.w
 | 
			
		||||
	if !w.ivSend {
 | 
			
		||||
		w.ivSend = true
 | 
			
		||||
		_, errRet = w.w.Write(w.iv)
 | 
			
		||||
		if errRet != nil {
 | 
			
		||||
			w.err = errRet
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nRet, errRet = w.enc.Write(p)
 | 
			
		||||
	if errRet != nil {
 | 
			
		||||
		w.err = errRet
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										124
									
								
								vendor/github.com/fatedier/golib/io/io.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										124
									
								
								vendor/github.com/fatedier/golib/io/io.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,124 +0,0 @@
 | 
			
		||||
// Copyright 2017 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 io
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/golib/crypto"
 | 
			
		||||
	"github.com/fatedier/golib/pool"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Join two io.ReadWriteCloser and do some operations.
 | 
			
		||||
func Join(c1 io.ReadWriteCloser, c2 io.ReadWriteCloser) (inCount int64, outCount int64) {
 | 
			
		||||
	var wait sync.WaitGroup
 | 
			
		||||
	pipe := func(to io.ReadWriteCloser, from io.ReadWriteCloser, count *int64) {
 | 
			
		||||
		defer to.Close()
 | 
			
		||||
		defer from.Close()
 | 
			
		||||
		defer wait.Done()
 | 
			
		||||
 | 
			
		||||
		buf := pool.GetBuf(16 * 1024)
 | 
			
		||||
		defer pool.PutBuf(buf)
 | 
			
		||||
		*count, _ = io.CopyBuffer(to, from, buf)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wait.Add(2)
 | 
			
		||||
	go pipe(c1, c2, &inCount)
 | 
			
		||||
	go pipe(c2, c1, &outCount)
 | 
			
		||||
	wait.Wait()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithEncryption(rwc io.ReadWriteCloser, key []byte) (io.ReadWriteCloser, error) {
 | 
			
		||||
	w, err := crypto.NewWriter(rwc, key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return WrapReadWriteCloser(crypto.NewReader(rwc, key), w, func() error {
 | 
			
		||||
		return rwc.Close()
 | 
			
		||||
	}), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithCompression(rwc io.ReadWriteCloser) io.ReadWriteCloser {
 | 
			
		||||
	sr := pool.GetSnappyReader(rwc)
 | 
			
		||||
	sw := pool.GetSnappyWriter(rwc)
 | 
			
		||||
	return WrapReadWriteCloser(sr, sw, func() error {
 | 
			
		||||
		err := rwc.Close()
 | 
			
		||||
		pool.PutSnappyReader(sr)
 | 
			
		||||
		pool.PutSnappyWriter(sw)
 | 
			
		||||
		return err
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReadWriteCloser struct {
 | 
			
		||||
	r       io.Reader
 | 
			
		||||
	w       io.Writer
 | 
			
		||||
	closeFn func() error
 | 
			
		||||
 | 
			
		||||
	closed bool
 | 
			
		||||
	mu     sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeFn will be called only once
 | 
			
		||||
func WrapReadWriteCloser(r io.Reader, w io.Writer, closeFn func() error) io.ReadWriteCloser {
 | 
			
		||||
	return &ReadWriteCloser{
 | 
			
		||||
		r:       r,
 | 
			
		||||
		w:       w,
 | 
			
		||||
		closeFn: closeFn,
 | 
			
		||||
		closed:  false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rwc *ReadWriteCloser) Read(p []byte) (n int, err error) {
 | 
			
		||||
	return rwc.r.Read(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rwc *ReadWriteCloser) Write(p []byte) (n int, err error) {
 | 
			
		||||
	return rwc.w.Write(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rwc *ReadWriteCloser) Close() (errRet error) {
 | 
			
		||||
	rwc.mu.Lock()
 | 
			
		||||
	if rwc.closed {
 | 
			
		||||
		rwc.mu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	rwc.closed = true
 | 
			
		||||
	rwc.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	if rc, ok := rwc.r.(io.Closer); ok {
 | 
			
		||||
		err = rc.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			errRet = err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if wc, ok := rwc.w.(io.Closer); ok {
 | 
			
		||||
		err = wc.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			errRet = err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if rwc.closeFn != nil {
 | 
			
		||||
		err = rwc.closeFn()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			errRet = err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								vendor/github.com/fatedier/golib/msg/json/msg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/fatedier/golib/msg/json/msg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,50 +0,0 @@
 | 
			
		||||
// 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 json
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	defaultMaxMsgLength int64 = 10240
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Message wraps socket packages for communicating between frpc and frps.
 | 
			
		||||
type Message interface{}
 | 
			
		||||
 | 
			
		||||
type MsgCtl struct {
 | 
			
		||||
	typeMap     map[byte]reflect.Type
 | 
			
		||||
	typeByteMap map[reflect.Type]byte
 | 
			
		||||
 | 
			
		||||
	maxMsgLength int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMsgCtl() *MsgCtl {
 | 
			
		||||
	return &MsgCtl{
 | 
			
		||||
		typeMap:      make(map[byte]reflect.Type),
 | 
			
		||||
		typeByteMap:  make(map[reflect.Type]byte),
 | 
			
		||||
		maxMsgLength: defaultMaxMsgLength,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) RegisterMsg(typeByte byte, msg interface{}) {
 | 
			
		||||
	msgCtl.typeMap[typeByte] = reflect.TypeOf(msg)
 | 
			
		||||
	msgCtl.typeByteMap[reflect.TypeOf(msg)] = typeByte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) SetMaxMsgLength(length int64) {
 | 
			
		||||
	msgCtl.maxMsgLength = length
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								vendor/github.com/fatedier/golib/msg/json/pack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/fatedier/golib/msg/json/pack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,66 +0,0 @@
 | 
			
		||||
// 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 json
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"reflect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) unpack(typeByte byte, buffer []byte, msgIn Message) (msg Message, err error) {
 | 
			
		||||
	if msgIn == nil {
 | 
			
		||||
		t, ok := msgCtl.typeMap[typeByte]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			err = ErrMsgType
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg = reflect.New(t).Interface().(Message)
 | 
			
		||||
	} else {
 | 
			
		||||
		msg = msgIn
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.Unmarshal(buffer, &msg)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) UnPackInto(buffer []byte, msg Message) (err error) {
 | 
			
		||||
	_, err = msgCtl.unpack(' ', buffer, msg)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) UnPack(typeByte byte, buffer []byte) (msg Message, err error) {
 | 
			
		||||
	return msgCtl.unpack(typeByte, buffer, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) Pack(msg Message) ([]byte, error) {
 | 
			
		||||
	typeByte, ok := msgCtl.typeByteMap[reflect.TypeOf(msg).Elem()]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, ErrMsgType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	content, err := json.Marshal(msg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buffer := bytes.NewBuffer(nil)
 | 
			
		||||
	buffer.WriteByte(typeByte)
 | 
			
		||||
	binary.Write(buffer, binary.BigEndian, int64(len(content)))
 | 
			
		||||
	buffer.Write(content)
 | 
			
		||||
	return buffer.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								vendor/github.com/fatedier/golib/msg/json/process.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										93
									
								
								vendor/github.com/fatedier/golib/msg/json/process.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,93 +0,0 @@
 | 
			
		||||
// 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 json
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrMsgType      = errors.New("message type error")
 | 
			
		||||
	ErrMaxMsgLength = errors.New("message length exceed the limit")
 | 
			
		||||
	ErrMsgLength    = errors.New("message length error")
 | 
			
		||||
	ErrMsgFormat    = errors.New("message format error")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) readMsg(c io.Reader) (typeByte byte, buffer []byte, err error) {
 | 
			
		||||
	buffer = make([]byte, 1)
 | 
			
		||||
	_, err = c.Read(buffer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	typeByte = buffer[0]
 | 
			
		||||
	if _, ok := msgCtl.typeMap[typeByte]; !ok {
 | 
			
		||||
		err = ErrMsgType
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var length int64
 | 
			
		||||
	err = binary.Read(c, binary.BigEndian, &length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if length > msgCtl.maxMsgLength {
 | 
			
		||||
		err = ErrMaxMsgLength
 | 
			
		||||
		return
 | 
			
		||||
	} else if length < 0 {
 | 
			
		||||
		err = ErrMsgLength
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buffer = make([]byte, length)
 | 
			
		||||
	n, err := io.ReadFull(c, buffer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if int64(n) != length {
 | 
			
		||||
		err = ErrMsgFormat
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) ReadMsg(c io.Reader) (msg Message, err error) {
 | 
			
		||||
	typeByte, buffer, err := msgCtl.readMsg(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return msgCtl.UnPack(typeByte, buffer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) ReadMsgInto(c io.Reader, msg Message) (err error) {
 | 
			
		||||
	_, buffer, err := msgCtl.readMsg(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return msgCtl.UnPackInto(buffer, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msgCtl *MsgCtl) WriteMsg(c io.Writer, msg interface{}) (err error) {
 | 
			
		||||
	buffer, err := msgCtl.Pack(msg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err = c.Write(buffer); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								vendor/github.com/fatedier/golib/net/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										64
									
								
								vendor/github.com/fatedier/golib/net/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,64 +0,0 @@
 | 
			
		||||
// 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 net
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SharedConn struct {
 | 
			
		||||
	net.Conn
 | 
			
		||||
	buf *bytes.Buffer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// the bytes you read in io.Reader, will be reserved in SharedConn
 | 
			
		||||
func NewSharedConn(conn net.Conn) (*SharedConn, io.Reader) {
 | 
			
		||||
	sc := &SharedConn{
 | 
			
		||||
		Conn: conn,
 | 
			
		||||
		buf:  bytes.NewBuffer(make([]byte, 0, 1024)),
 | 
			
		||||
	}
 | 
			
		||||
	return sc, io.TeeReader(conn, sc.buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSharedConnSize(conn net.Conn, bufSize int) (*SharedConn, io.Reader) {
 | 
			
		||||
	sc := &SharedConn{
 | 
			
		||||
		Conn: conn,
 | 
			
		||||
		buf:  bytes.NewBuffer(make([]byte, 0, bufSize)),
 | 
			
		||||
	}
 | 
			
		||||
	return sc, io.TeeReader(conn, sc.buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Not thread safety.
 | 
			
		||||
func (sc *SharedConn) Read(p []byte) (n int, err error) {
 | 
			
		||||
	if sc.buf == nil {
 | 
			
		||||
		return sc.Conn.Read(p)
 | 
			
		||||
	}
 | 
			
		||||
	n, err = sc.buf.Read(p)
 | 
			
		||||
	if err == io.EOF {
 | 
			
		||||
		sc.buf = nil
 | 
			
		||||
		var n2 int
 | 
			
		||||
		n2, err = sc.Conn.Read(p[n:])
 | 
			
		||||
		n += n2
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *SharedConn) ResetBuf(buffer []byte) (err error) {
 | 
			
		||||
	sc.buf.Reset()
 | 
			
		||||
	_, err = sc.buf.Write(buffer)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										232
									
								
								vendor/github.com/fatedier/golib/net/mux/mux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										232
									
								
								vendor/github.com/fatedier/golib/net/mux/mux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,232 +0,0 @@
 | 
			
		||||
// 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 mux
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/golib/errors"
 | 
			
		||||
	gnet "github.com/fatedier/golib/net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// DefaultTimeout is the default length of time to wait for bytes we need.
 | 
			
		||||
	DefaultTimeout = 10 * time.Second
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Mux struct {
 | 
			
		||||
	ln net.Listener
 | 
			
		||||
 | 
			
		||||
	defaultLn *listener
 | 
			
		||||
 | 
			
		||||
	// sorted by priority
 | 
			
		||||
	lns             []*listener
 | 
			
		||||
	maxNeedBytesNum uint32
 | 
			
		||||
 | 
			
		||||
	mu sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMux(ln net.Listener) (mux *Mux) {
 | 
			
		||||
	mux = &Mux{
 | 
			
		||||
		ln:  ln,
 | 
			
		||||
		lns: make([]*listener, 0),
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// priority
 | 
			
		||||
func (mux *Mux) Listen(priority int, needBytesNum uint32, fn MatchFunc) net.Listener {
 | 
			
		||||
	ln := &listener{
 | 
			
		||||
		c:            make(chan net.Conn),
 | 
			
		||||
		mux:          mux,
 | 
			
		||||
		priority:     priority,
 | 
			
		||||
		needBytesNum: needBytesNum,
 | 
			
		||||
		matchFn:      fn,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mux.mu.Lock()
 | 
			
		||||
	defer mux.mu.Unlock()
 | 
			
		||||
	if needBytesNum > mux.maxNeedBytesNum {
 | 
			
		||||
		mux.maxNeedBytesNum = needBytesNum
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newlns := append(mux.copyLns(), ln)
 | 
			
		||||
	sort.Slice(newlns, func(i, j int) bool {
 | 
			
		||||
		if newlns[i].priority == newlns[j].priority {
 | 
			
		||||
			return newlns[i].needBytesNum < newlns[j].needBytesNum
 | 
			
		||||
		}
 | 
			
		||||
		return newlns[i].priority < newlns[j].priority
 | 
			
		||||
	})
 | 
			
		||||
	mux.lns = newlns
 | 
			
		||||
	return ln
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) ListenHttp(priority int) net.Listener {
 | 
			
		||||
	return mux.Listen(priority, HttpNeedBytesNum, HttpMatchFunc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) ListenHttps(priority int) net.Listener {
 | 
			
		||||
	return mux.Listen(priority, HttpsNeedBytesNum, HttpsMatchFunc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) DefaultListener() net.Listener {
 | 
			
		||||
	mux.mu.Lock()
 | 
			
		||||
	defer mux.mu.Unlock()
 | 
			
		||||
	if mux.defaultLn == nil {
 | 
			
		||||
		mux.defaultLn = &listener{
 | 
			
		||||
			c:   make(chan net.Conn),
 | 
			
		||||
			mux: mux,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return mux.defaultLn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) release(ln *listener) bool {
 | 
			
		||||
	result := false
 | 
			
		||||
	mux.mu.Lock()
 | 
			
		||||
	defer mux.mu.Unlock()
 | 
			
		||||
	lns := mux.copyLns()
 | 
			
		||||
 | 
			
		||||
	for i, l := range lns {
 | 
			
		||||
		if l == ln {
 | 
			
		||||
			lns = append(lns[:i], lns[i+1:]...)
 | 
			
		||||
			result = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	mux.lns = lns
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) copyLns() []*listener {
 | 
			
		||||
	lns := make([]*listener, 0, len(mux.lns))
 | 
			
		||||
	for _, l := range mux.lns {
 | 
			
		||||
		lns = append(lns, l)
 | 
			
		||||
	}
 | 
			
		||||
	return lns
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serve handles connections from ln and multiplexes then across registered listeners.
 | 
			
		||||
func (mux *Mux) Serve() error {
 | 
			
		||||
	for {
 | 
			
		||||
		// Wait for the next connection.
 | 
			
		||||
		// If it returns a temporary error then simply retry.
 | 
			
		||||
		// If it returns any other error then exit immediately.
 | 
			
		||||
		conn, err := mux.ln.Accept()
 | 
			
		||||
		if err, ok := err.(interface {
 | 
			
		||||
			Temporary() bool
 | 
			
		||||
		}); ok && err.Temporary() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		go mux.handleConn(conn)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) handleConn(conn net.Conn) {
 | 
			
		||||
	mux.mu.RLock()
 | 
			
		||||
	maxNeedBytesNum := mux.maxNeedBytesNum
 | 
			
		||||
	lns := mux.lns
 | 
			
		||||
	defaultLn := mux.defaultLn
 | 
			
		||||
	mux.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	sharedConn, rd := gnet.NewSharedConnSize(conn, int(maxNeedBytesNum))
 | 
			
		||||
	data := make([]byte, maxNeedBytesNum)
 | 
			
		||||
 | 
			
		||||
	conn.SetReadDeadline(time.Now().Add(DefaultTimeout))
 | 
			
		||||
	_, err := io.ReadFull(rd, data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	conn.SetReadDeadline(time.Time{})
 | 
			
		||||
 | 
			
		||||
	for _, ln := range lns {
 | 
			
		||||
		if match := ln.matchFn(data); match {
 | 
			
		||||
			err = errors.PanicToError(func() {
 | 
			
		||||
				ln.c <- sharedConn
 | 
			
		||||
			})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				conn.Close()
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No match listeners
 | 
			
		||||
	if defaultLn != nil {
 | 
			
		||||
		err = errors.PanicToError(func() {
 | 
			
		||||
			defaultLn.c <- sharedConn
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			conn.Close()
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No listeners for this connection, close it.
 | 
			
		||||
	conn.Close()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type listener struct {
 | 
			
		||||
	mux *Mux
 | 
			
		||||
 | 
			
		||||
	priority     int
 | 
			
		||||
	needBytesNum uint32
 | 
			
		||||
	matchFn      MatchFunc
 | 
			
		||||
 | 
			
		||||
	c  chan net.Conn
 | 
			
		||||
	mu sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Accept waits for and returns the next connection to the listener.
 | 
			
		||||
func (ln *listener) Accept() (net.Conn, error) {
 | 
			
		||||
	conn, ok := <-ln.c
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("network connection closed")
 | 
			
		||||
	}
 | 
			
		||||
	return conn, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes this listener from the parent mux and closes the channel.
 | 
			
		||||
func (ln *listener) Close() error {
 | 
			
		||||
	if ok := ln.mux.release(ln); ok {
 | 
			
		||||
		// Close done to signal to any RLock holders to release their lock.
 | 
			
		||||
		close(ln.c)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ln *listener) Addr() net.Addr {
 | 
			
		||||
	if ln.mux == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	ln.mux.mu.RLock()
 | 
			
		||||
	defer ln.mux.mu.RUnlock()
 | 
			
		||||
	if ln.mux.ln == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return ln.mux.ln.Addr()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								vendor/github.com/fatedier/golib/net/mux/rule.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/fatedier/golib/net/mux/rule.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,55 +0,0 @@
 | 
			
		||||
package mux
 | 
			
		||||
 | 
			
		||||
type MatchFunc func(data []byte) (match bool)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	HttpsNeedBytesNum uint32 = 1
 | 
			
		||||
	HttpNeedBytesNum  uint32 = 3
 | 
			
		||||
	YamuxNeedBytesNum uint32 = 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var HttpsMatchFunc MatchFunc = func(data []byte) bool {
 | 
			
		||||
	if len(data) < int(HttpsNeedBytesNum) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if data[0] == 0x16 {
 | 
			
		||||
		return true
 | 
			
		||||
	} else {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// From https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
 | 
			
		||||
var httpHeadBytes = map[string]struct{}{
 | 
			
		||||
	"GET": struct{}{},
 | 
			
		||||
	"HEA": struct{}{},
 | 
			
		||||
	"POS": struct{}{},
 | 
			
		||||
	"PUT": struct{}{},
 | 
			
		||||
	"DEL": struct{}{},
 | 
			
		||||
	"CON": struct{}{},
 | 
			
		||||
	"OPT": struct{}{},
 | 
			
		||||
	"TRA": struct{}{},
 | 
			
		||||
	"PAT": struct{}{},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var HttpMatchFunc MatchFunc = func(data []byte) bool {
 | 
			
		||||
	if len(data) < int(HttpNeedBytesNum) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, ok := httpHeadBytes[string(data[:3])]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// From https://github.com/hashicorp/yamux/blob/master/spec.md
 | 
			
		||||
var YamuxMatchFunc MatchFunc = func(data []byte) bool {
 | 
			
		||||
	if len(data) < int(YamuxNeedBytesNum) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if data[0] == 0 && data[1] >= 0x0 && data[1] <= 0x3 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										107
									
								
								vendor/github.com/fatedier/golib/net/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										107
									
								
								vendor/github.com/fatedier/golib/net/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,107 +0,0 @@
 | 
			
		||||
// 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 net
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/proxy"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ProxyAuth struct {
 | 
			
		||||
	Enable   bool
 | 
			
		||||
	Username string
 | 
			
		||||
	Passwd   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DialTcpByProxy(proxyStr string, addr string) (c net.Conn, err error) {
 | 
			
		||||
	if proxyStr == "" {
 | 
			
		||||
		return net.Dial("tcp", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var proxyUrl *url.URL
 | 
			
		||||
	if proxyUrl, err = url.Parse(proxyStr); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auth := &ProxyAuth{}
 | 
			
		||||
	if proxyUrl.User != nil {
 | 
			
		||||
		auth.Enable = true
 | 
			
		||||
		auth.Username = proxyUrl.User.Username()
 | 
			
		||||
		auth.Passwd, _ = proxyUrl.User.Password()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch proxyUrl.Scheme {
 | 
			
		||||
	case "http":
 | 
			
		||||
		return DialTcpByHttpProxy(proxyUrl.Host, addr, auth)
 | 
			
		||||
	case "socks5":
 | 
			
		||||
		return DialTcpBySocks5Proxy(proxyUrl.Host, addr, auth)
 | 
			
		||||
	default:
 | 
			
		||||
		err = fmt.Errorf("Proxy URL scheme must be http or socks5, not [%s]", proxyUrl.Scheme)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DialTcpByHttpProxy(proxyHost string, dstAddr string, auth *ProxyAuth) (c net.Conn, err error) {
 | 
			
		||||
	if c, err = net.Dial("tcp", proxyHost); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequest("CONNECT", "http://"+dstAddr, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if auth.Enable {
 | 
			
		||||
		req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth.Username+":"+auth.Passwd)))
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("User-Agent", "Mozilla/5.0")
 | 
			
		||||
	req.Write(c)
 | 
			
		||||
 | 
			
		||||
	resp, err := http.ReadResponse(bufio.NewReader(c), req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	resp.Body.Close()
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
		err = fmt.Errorf("DialTcpByHttpProxy error, StatusCode [%d]", resp.StatusCode)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DialTcpBySocks5Proxy(proxyHost string, dstAddr string, auth *ProxyAuth) (c net.Conn, err error) {
 | 
			
		||||
	var s5Auth *proxy.Auth
 | 
			
		||||
	if auth.Enable {
 | 
			
		||||
		s5Auth = &proxy.Auth{
 | 
			
		||||
			User:     auth.Username,
 | 
			
		||||
			Password: auth.Passwd,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dialer, err := proxy.SOCKS5("tcp", proxyHost, s5Auth, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c, err = dialer.Dial("tcp", dstAddr); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user