Browse Source

Merge pull request #1494 from fatedier/dev

bump version to v0.29.1
fatedier 6 years ago
parent
commit
adc3adc13b

+ 1 - 1
Makefile

@@ -16,7 +16,7 @@ file:
 
 
 fmt:
 fmt:
 	go fmt ./...
 	go fmt ./...
-	
+
 frps:
 frps:
 	go build -o bin/frps ./cmd/frps
 	go build -o bin/frps ./cmd/frps
 
 

+ 178 - 179
README.md

@@ -6,53 +6,53 @@
 
 
 ## What is frp?
 ## What is frp?
 
 
-frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. As of now, it supports tcp & udp, as well as http and https protocols, where requests can be forwarded to internal services by domain name.
+frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name.
 
 
-Now it also try to support p2p connect.
+frp also has a P2P connect mode.
 
 
 ## Table of Contents
 ## Table of Contents
 
 
 <!-- vim-markdown-toc GFM -->
 <!-- vim-markdown-toc GFM -->
 
 
-* [Status](#status)
+* [Development Status](#development-status)
 * [Architecture](#architecture)
 * [Architecture](#architecture)
 * [Example Usage](#example-usage)
 * [Example Usage](#example-usage)
     * [Access your computer in LAN by SSH](#access-your-computer-in-lan-by-ssh)
     * [Access your computer in LAN by SSH](#access-your-computer-in-lan-by-ssh)
     * [Visit your web service in LAN by custom domains](#visit-your-web-service-in-lan-by-custom-domains)
     * [Visit your web service in LAN by custom domains](#visit-your-web-service-in-lan-by-custom-domains)
     * [Forward DNS query request](#forward-dns-query-request)
     * [Forward DNS query request](#forward-dns-query-request)
-    * [Forward unix domain socket](#forward-unix-domain-socket)
-    * [Expose a simple http file server](#expose-a-simple-http-file-server)
+    * [Forward Unix domain socket](#forward-unix-domain-socket)
+    * [Expose a simple HTTP file server](#expose-a-simple-http-file-server)
     * [Enable HTTPS for local HTTP service](#enable-https-for-local-http-service)
     * [Enable HTTPS for local HTTP service](#enable-https-for-local-http-service)
-    * [Expose your service in security](#expose-your-service-in-security)
+    * [Expose your service privately](#expose-your-service-privately)
     * [P2P Mode](#p2p-mode)
     * [P2P Mode](#p2p-mode)
 * [Features](#features)
 * [Features](#features)
-    * [Configuration File](#configuration-file)
-    * [Configuration file template](#configuration-file-template)
+    * [Configuration Files](#configuration-files)
+    * [Using Environment Variables](#using-environment-variables)
     * [Dashboard](#dashboard)
     * [Dashboard](#dashboard)
     * [Admin UI](#admin-ui)
     * [Admin UI](#admin-ui)
-    * [Authentication](#authentication)
+    * [Authenticating the Client](#authenticating-the-client)
     * [Encryption and Compression](#encryption-and-compression)
     * [Encryption and Compression](#encryption-and-compression)
         * [TLS](#tls)
         * [TLS](#tls)
-    * [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
+    * [Hot-Reloading frpc configuration](#hot-reloading-frpc-configuration)
     * [Get proxy status from client](#get-proxy-status-from-client)
     * [Get proxy status from client](#get-proxy-status-from-client)
-    * [Port White List](#port-white-list)
+    * [Only allowing certain ports on the server](#only-allowing-certain-ports-on-the-server)
     * [Port Reuse](#port-reuse)
     * [Port Reuse](#port-reuse)
     * [TCP Stream Multiplexing](#tcp-stream-multiplexing)
     * [TCP Stream Multiplexing](#tcp-stream-multiplexing)
     * [Support KCP Protocol](#support-kcp-protocol)
     * [Support KCP Protocol](#support-kcp-protocol)
-    * [Connection Pool](#connection-pool)
+    * [Connection Pooling](#connection-pooling)
     * [Load balancing](#load-balancing)
     * [Load balancing](#load-balancing)
-    * [Health Check](#health-check)
-    * [Rewriting the Host Header](#rewriting-the-host-header)
-    * [Set Headers In HTTP Request](#set-headers-in-http-request)
+    * [Service Health Check](#service-health-check)
+    * [Rewriting the HTTP Host Header](#rewriting-the-http-host-header)
+    * [Setting other HTTP Headers](#setting-other-http-headers)
     * [Get Real IP](#get-real-ip)
     * [Get Real IP](#get-real-ip)
         * [HTTP X-Forwarded-For](#http-x-forwarded-for)
         * [HTTP X-Forwarded-For](#http-x-forwarded-for)
         * [Proxy Protocol](#proxy-protocol)
         * [Proxy Protocol](#proxy-protocol)
-    * [Password protecting your web service](#password-protecting-your-web-service)
+    * [Require HTTP Basic auth (password) for web services](#require-http-basic-auth-password-for-web-services)
     * [Custom subdomain names](#custom-subdomain-names)
     * [Custom subdomain names](#custom-subdomain-names)
     * [URL routing](#url-routing)
     * [URL routing](#url-routing)
-    * [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
+    * [Connecting to frps via HTTP PROXY](#connecting-to-frps-via-http-proxy)
     * [Range ports mapping](#range-ports-mapping)
     * [Range ports mapping](#range-ports-mapping)
-    * [Plugin](#plugin)
+    * [Plugins](#plugins)
 * [Development Plan](#development-plan)
 * [Development Plan](#development-plan)
 * [Contributing](#contributing)
 * [Contributing](#contributing)
 * [Donation](#donation)
 * [Donation](#donation)
@@ -62,11 +62,11 @@ Now it also try to support p2p connect.
 
 
 <!-- vim-markdown-toc -->
 <!-- vim-markdown-toc -->
 
 
-## Status
+## Development Status
 
 
-frp is under development and you can try it with latest release version. Master branch for releasing stable version when dev branch for developing.
+frp is under development. Try the latest release version in the `master` branch, or use the `dev` branch for the version in development.
 
 
-**We may change any protocol and can't promise backward compatibility. Please check the release log when upgrading.**
+**The protocol might change at a release and we don't promise backwards compatibility. Please check the release log when upgrading the client and the server.**
 
 
 ## Architecture
 ## Architecture
 
 
@@ -74,15 +74,15 @@ frp is under development and you can try it with latest release version. Master
 
 
 ## Example Usage
 ## Example Usage
 
 
-Firstly, download the latest programs from [Release](https://github.com/fatedier/frp/releases) page according to your os and arch.
+Firstly, download the latest programs from [Release](https://github.com/fatedier/frp/releases) page according to your operating system and architecture.
 
 
-Put **frps** and **frps.ini** to your server with public IP.
+Put `frps` and `frps.ini` onto your server A with public IP.
 
 
-Put **frpc** and **frpc.ini** to your server in LAN.
+Put `frpc` and `frpc.ini` onto your server B in LAN (that can't be connected from public Internet).
 
 
 ### Access your computer in LAN by SSH
 ### Access your computer in LAN by SSH
 
 
-1. Modify frps.ini:
+1. Modify `frps.ini` on server A:
 
 
   ```ini
   ```ini
   # frps.ini
   # frps.ini
@@ -90,11 +90,11 @@ Put **frpc** and **frpc.ini** to your server in LAN.
   bind_port = 7000
   bind_port = 7000
   ```
   ```
 
 
-2. Start frps:
+2. Start `frps` on server A:
 
 
   `./frps -c ./frps.ini`
   `./frps -c ./frps.ini`
 
 
-3. Modify frpc.ini, `server_addr` is your frps's server IP:
+3. On server B, modify `frpc.ini` to put in your `frps` server public IP as `server_addr` field:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -109,21 +109,21 @@ Put **frpc** and **frpc.ini** to your server in LAN.
   remote_port = 6000
   remote_port = 6000
   ```
   ```
 
 
-4. Start frpc:
+4. Start `frpc` on server B:
 
 
   `./frpc -c ./frpc.ini`
   `./frpc -c ./frpc.ini`
 
 
-5. Connect to server in LAN by ssh assuming that username is test:
+5. From another machine, SSH to server B like this (assuming that username is `test`):
 
 
   `ssh -oPort=6000 test@x.x.x.x`
   `ssh -oPort=6000 test@x.x.x.x`
 
 
 ### Visit your web service in LAN by custom domains
 ### Visit your web service in LAN by custom domains
 
 
-Sometimes we want to expose a local web service behind a NAT network to others for testing with your own domain name and unfortunately we can't resolve a domain name to a local ip.
+Sometimes we want to expose a local web service behind a NAT network to others for testing with your own domain name and unfortunately we can't resolve a domain name to a local IP.
 
 
-However, we can expose a http or https service using frp.
+However, we can expose an HTTP(S) service using frp.
 
 
-1. Modify frps.ini, configure http port 8080:
+1. Modify `frps.ini`, set the vhost HTTP port to 8080:
 
 
   ```ini
   ```ini
   # frps.ini
   # frps.ini
@@ -132,11 +132,11 @@ However, we can expose a http or https service using frp.
   vhost_http_port = 8080
   vhost_http_port = 8080
   ```
   ```
 
 
-2. Start frps:
+2. Start `frps`:
 
 
   `./frps -c ./frps.ini`
   `./frps -c ./frps.ini`
 
 
-3. Modify frpc.ini and set remote frps server's IP as x.x.x.x. The `local_port` is the port of your web service:
+3. Modify `frpc.ini` and set `server_addr` to the IP address of the remote frps server. The `local_port` is the port of your web service:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -147,20 +147,20 @@ However, we can expose a http or https service using frp.
   [web]
   [web]
   type = http
   type = http
   local_port = 80
   local_port = 80
-  custom_domains = www.yourdomain.com
+  custom_domains = www.example.com
   ```
   ```
 
 
-4. Start frpc:
+4. Start `frpc`:
 
 
   `./frpc -c ./frpc.ini`
   `./frpc -c ./frpc.ini`
 
 
-5. Resolve A record of `www.yourdomain.com` to IP `x.x.x.x` or CNAME record to your origin domain.
+5. Resolve A record of `www.example.com` to the public IP of the remote frps server or CNAME record to your origin domain.
 
 
-6. Now visit your local web service using url `http://www.yourdomain.com:8080`.
+6. Now visit your local web service using url `http://www.example.com:8080`.
 
 
 ### Forward DNS query request
 ### Forward DNS query request
 
 
-1. Modify frps.ini:
+1. Modify `frps.ini`:
 
 
   ```ini
   ```ini
   # frps.ini
   # frps.ini
@@ -168,11 +168,11 @@ However, we can expose a http or https service using frp.
   bind_port = 7000
   bind_port = 7000
   ```
   ```
 
 
-2. Start frps:
+2. Start `frps`:
 
 
   `./frps -c ./frps.ini`
   `./frps -c ./frps.ini`
 
 
-3. Modify frpc.ini, set remote frps's server IP as x.x.x.x, forward dns query request to google dns server `8.8.8.8:53`:
+3. Modify `frpc.ini` and set `server_addr` to the IP address of the remote frps server, forward DNS query request to Google Public DNS server `8.8.8.8:53`:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -191,17 +191,17 @@ However, we can expose a http or https service using frp.
 
 
   `./frpc -c ./frpc.ini`
   `./frpc -c ./frpc.ini`
 
 
-5. Send dns query request by dig:
+5. Test DNS resolution using `dig` command:
 
 
   `dig @x.x.x.x -p 6000 www.google.com`
   `dig @x.x.x.x -p 6000 www.google.com`
 
 
-### Forward unix domain socket
+### Forward Unix domain socket
 
 
-Using tcp port to connect unix domain socket like docker daemon.
+Expose a Unix domain socket (e.g. the Docker daemon socket) as TCP.
 
 
-Configure frps same as above.
+Configure `frps` same as above.
 
 
-1. Start frpc with configurations:
+1. Start `frpc` with configuration:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -216,17 +216,17 @@ Configure frps same as above.
   plugin_unix_path = /var/run/docker.sock
   plugin_unix_path = /var/run/docker.sock
   ```
   ```
 
 
-2. Get docker version by curl command:
+2. Test: Get Docker version using `curl`:
 
 
   `curl http://x.x.x.x:6000/version`
   `curl http://x.x.x.x:6000/version`
 
 
-### Expose a simple http file server
+### Expose a simple HTTP file server
 
 
-A simple way to visit files in the LAN.
+Browser your files stored in the LAN, from public Internet.
 
 
-Configure frps same as above.
+Configure `frps` same as above.
 
 
-1. Start frpc with configurations:
+1. Start `frpc` with configuration:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -238,17 +238,17 @@ Configure frps same as above.
   type = tcp
   type = tcp
   remote_port = 6000
   remote_port = 6000
   plugin = static_file
   plugin = static_file
-  plugin_local_path = /tmp/file
+  plugin_local_path = /tmp/files
   plugin_strip_prefix = static
   plugin_strip_prefix = static
   plugin_http_user = abc
   plugin_http_user = abc
   plugin_http_passwd = abc
   plugin_http_passwd = abc
   ```
   ```
 
 
-2. Visit `http://x.x.x.x:6000/static/` by your browser, set correct user and password, so you can see files in `/tmp/file`.
+2. Visit `http://x.x.x.x:6000/static/` from your browser and specify correct user and password to view files in `/tmp/files` on the `frpc` machine.
 
 
 ### Enable HTTPS for local HTTP service
 ### Enable HTTPS for local HTTP service
 
 
-1. Start frpc with configurations:
+1. Start `frpc` with configuration:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -256,9 +256,9 @@ Configure frps same as above.
   server_addr = x.x.x.x
   server_addr = x.x.x.x
   server_port = 7000
   server_port = 7000
 
 
-  [test_htts2http]
+  [test_https2http]
   type = https
   type = https
-  custom_domains = test.yourdomain.com
+  custom_domains = test.example.com
 
 
   plugin = https2http
   plugin = https2http
   plugin_local_addr = 127.0.0.1:80
   plugin_local_addr = 127.0.0.1:80
@@ -268,17 +268,15 @@ Configure frps same as above.
   plugin_header_X-From-Where = frp
   plugin_header_X-From-Where = frp
   ```
   ```
 
 
-2. Visit `https://test.yourdomain.com`.
+2. Visit `https://test.example.com`.
 
 
-### Expose your service in security
+### Expose your service privately
 
 
-For some services, if expose them to the public network directly will be a security risk.
+Some services will be at risk if exposed directly to the public network. With **STCP** (secret TCP) mode, a preshared key is needed to access the service from another client.
 
 
-**stcp(secret tcp)** helps you create a proxy avoiding any one can access it.
+Configure `frps` same as above.
 
 
-Configure frps same as above.
-
-1. Start frpc, forward ssh port and `remote_port` is useless:
+1. Start `frpc` on machine B with the following config. This example is for exposing the SSH service (port 22), and note the `sk` field for the preshared key, and that the `remote_port` field is removed here:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -293,7 +291,7 @@ Configure frps same as above.
   local_port = 22
   local_port = 22
   ```
   ```
 
 
-2. Start another frpc in which you want to connect this ssh server:
+2. Start another `frpc` (typically on another machine C) with the following config to access the SSH service with a security key (`sk` field):
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -310,23 +308,24 @@ Configure frps same as above.
   bind_port = 6000
   bind_port = 6000
   ```
   ```
 
 
-3. Connect to server in LAN by ssh assuming that username is test:
+3. On machine C, connect to SSH on machine B, using this command:
 
 
-  `ssh -oPort=6000 test@127.0.0.1`
+  `ssh -oPort=6000 127.0.0.1`
 
 
 ### P2P Mode
 ### P2P Mode
 
 
-**xtcp** is designed for transmitting a large amount of data directly between two client.
+**xtcp** is designed for transmitting large amounts of data directly between clients. A frps server is still needed, as P2P here only refers the actual data transmission.
 
 
-Now it can't penetrate all types of NAT devices. You can try **stcp** if **xtcp** doesn't work.
+Note it can't penetrate all types of NAT devices. You might want to fallback to **stcp** if **xtcp** doesn't work.
 
 
-1. Configure a udp port for xtcp:
+1. In `frps.ini` configure a UDP port for xtcp:
 
 
   ```ini
   ```ini
+  # frps.ini
   bind_udp_port = 7001
   bind_udp_port = 7001
   ```
   ```
 
 
-2. Start frpc, forward ssh port and `remote_port` is useless:
+2. Start `frpc` on machine B, expose the SSH port. Note that `remote_port` field is removed:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -341,7 +340,7 @@ Now it can't penetrate all types of NAT devices. You can try **stcp** if **xtcp*
   local_port = 22
   local_port = 22
   ```
   ```
 
 
-3. Start another frpc in which you want to connect this ssh server:
+3. Start another `frpc` (typically on another machine C) with the config to connect to SSH using P2P mode:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
@@ -358,23 +357,23 @@ Now it can't penetrate all types of NAT devices. You can try **stcp** if **xtcp*
   bind_port = 6000
   bind_port = 6000
   ```
   ```
 
 
-4. Connect to server in LAN by ssh assuming that username is test:
+4. On machine C, connect to SSH on machine B, using this command:
 
 
-  `ssh -oPort=6000 test@127.0.0.1`
+  `ssh -oPort=6000 127.0.0.1`
 
 
 ## Features
 ## Features
 
 
-### Configuration File
+### Configuration Files
 
 
-You can find features which this document not metioned from full example configuration files.
+Read the full example configuration files to find out even more features not described here.
 
 
-[frps full configuration file](./conf/frps_full.ini)
+[Full configuration file for frps (Server)](./conf/frps_full.ini)
 
 
-[frpc full configuration file](./conf/frpc_full.ini)
+[Full configuration file for frpc (Client)](./conf/frpc_full.ini)
 
 
-### Configuration file template
+### Using Environment Variables
 
 
-Configuration file tempalte can be rendered using os environments. Template uses Go's standard format.
+Environment variables can be referenced in the configuration file, using Go's standard format:
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
@@ -389,7 +388,7 @@ local_port = 22
 remote_port = {{ .Envs.FRP_SSH_REMOTE_PORT }}
 remote_port = {{ .Envs.FRP_SSH_REMOTE_PORT }}
 ```
 ```
 
 
-Start frpc program:
+With the config above, variables can be passed into `frpc` program like this:
 
 
 ```
 ```
 export FRP_SERVER_ADDR="x.x.x.x"
 export FRP_SERVER_ADDR="x.x.x.x"
@@ -397,12 +396,11 @@ export FRP_SSH_REMOTE_PORT="6000"
 ./frpc -c ./frpc.ini
 ./frpc -c ./frpc.ini
 ```
 ```
 
 
-frpc will auto render configuration file template using os environments.
-All environments has prefix `.Envs`.
+`frpc` will render configuration file template using OS environment variables. Remember to prefix your reference with `.Envs`.
 
 
 ### Dashboard
 ### Dashboard
 
 
-Check frp's status and proxies's statistics information by Dashboard.
+Check frp's status and proxies' statistics information by Dashboard.
 
 
 Configure a port for dashboard to enable this feature:
 Configure a port for dashboard to enable this feature:
 
 
@@ -414,15 +412,15 @@ dashboard_user = admin
 dashboard_pwd = admin
 dashboard_pwd = admin
 ```
 ```
 
 
-Then visit `http://[server_addr]:7500` to see dashboard, default username and password are both `admin`.
+Then visit `http://[server_addr]:7500` to see the dashboard, with username and password both being `admin` by default.
 
 
 ![dashboard](/doc/pic/dashboard.png)
 ![dashboard](/doc/pic/dashboard.png)
 
 
 ### Admin UI
 ### Admin UI
 
 
-Admin UI help you check and manage frpc's configure.
+The Admin UI helps you check and manage frpc's configuration.
 
 
-Configure a address for admin UI to enable this feature:
+Configure an address for admin UI to enable this feature:
 
 
 ```ini
 ```ini
 [common]
 [common]
@@ -432,15 +430,15 @@ admin_user = admin
 admin_pwd = admin
 admin_pwd = admin
 ```
 ```
 
 
-Then visit `http://127.0.0.1:7400` to see admin UI, default username and password are both `admin`.
+Then visit `http://127.0.0.1:7400` to see admin UI, with username and password both being `admin` by default.
 
 
-### Authentication
+### Authenticating the Client
 
 
-`token` in frps.ini and frpc.ini should be same.
+Always use the same `token` in the `[common]` section in `frps.ini` and `frpc.ini`.
 
 
 ### Encryption and Compression
 ### Encryption and Compression
 
 
-Defalut value is false, you could decide if the proxy will use encryption or compression:
+The features are off by default. You can turn on encryption and/or compression:
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
@@ -454,15 +452,15 @@ use_compression = true
 
 
 #### TLS
 #### TLS
 
 
-frp support TLS protocol between frpc and frps since v0.25.0.
+frp supports the TLS protocol between `frpc` and `frps` since v0.25.0.
 
 
-Config `tls_enable = true` in `common` section to frpc.ini to enable this feature.
+Config `tls_enable = true` in the `[common]` section to `frpc.ini` to enable this feature.
 
 
-For port multiplexing, frp send a first byte 0x17 to dial a TLS connection.
+For port multiplexing, frp sends a first byte `0x17` to dial a TLS connection.
 
 
-### Hot-Reload frpc configuration
+### Hot-Reloading frpc configuration
 
 
-First you need to set admin port in frpc's configure file to let it provide HTTP API for more features.
+The `admin_addr` and `admin_port` fields are required for enabling HTTP API:
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
@@ -471,17 +469,17 @@ admin_addr = 127.0.0.1
 admin_port = 7400
 admin_port = 7400
 ```
 ```
 
 
-Then run command `frpc reload -c ./frpc.ini` and wait for about 10 seconds to let frpc create or update or delete proxies.
+Then run command `frpc reload -c ./frpc.ini` and wait for about 10 seconds to let `frpc` create or update or delete proxies.
 
 
-**Note that parameters in [common] section won't be modified except 'start' now.**
+**Note that parameters in [common] section won't be modified except 'start'.**
 
 
 ### Get proxy status from client
 ### Get proxy status from client
 
 
-Use `frpc status -c ./frpc.ini` to get status of all proxies. You need to set admin port in frpc's configure file.
+Use `frpc status -c ./frpc.ini` to get status of all proxies. The `admin_addr` and `admin_port` fields are required for enabling HTTP API.
 
 
-### Port White List
+### Only allowing certain ports on the server
 
 
-`allow_ports` in frps.ini is used for preventing abuse of ports:
+`allow_ports` in `frps.ini` is used to avoid abuse of ports:
 
 
 ```ini
 ```ini
 # frps.ini
 # frps.ini
@@ -489,19 +487,19 @@ Use `frpc status -c ./frpc.ini` to get status of all proxies. You need to set ad
 allow_ports = 2000-3000,3001,3003,4000-50000
 allow_ports = 2000-3000,3001,3003,4000-50000
 ```
 ```
 
 
-`allow_ports` consists of a specific port or a range of ports divided by `,`.
+`allow_ports` consists of specific ports or port ranges (lowest port number, dash `-`, highest port number), separated by comma `,`.
 
 
 ### Port Reuse
 ### Port Reuse
 
 
-Now `vhost_http_port` and `vhost_https_port` in frps can use same port with `bind_port`. frps will detect connection's protocol and handle it correspondingly.
+`vhost_http_port` and `vhost_https_port` in frps can use same port with `bind_port`. frps will detect the connection's protocol and handle it correspondingly.
 
 
 We would like to try to allow multiple proxies bind a same remote port with different protocols in the future.
 We would like to try to allow multiple proxies bind a same remote port with different protocols in the future.
 
 
 ### TCP Stream Multiplexing
 ### TCP Stream Multiplexing
 
 
-frp support tcp stream multiplexing since v0.10.0 like HTTP2 Multiplexing. All user requests to same frpc can use only one tcp connection.
+frp supports tcp stream multiplexing since v0.10.0 like HTTP2 Multiplexing, in which case all logic connections to the same frpc are multiplexed into the same TCP connection.
 
 
-You can disable this feature by modify frps.ini and frpc.ini:
+You can disable this feature by modify `frps.ini` and `frpc.ini`:
 
 
 ```ini
 ```ini
 # frps.ini and frpc.ini, must be same
 # frps.ini and frpc.ini, must be same
@@ -513,36 +511,38 @@ tcp_mux = false
 
 
 KCP is a fast and reliable protocol that can achieve the transmission effect of a reduction of the average latency by 30% to 40% and reduction of the maximum delay by a factor of three, at the cost of 10% to 20% more bandwidth wasted than TCP.
 KCP is a fast and reliable protocol that can achieve the transmission effect of a reduction of the average latency by 30% to 40% and reduction of the maximum delay by a factor of three, at the cost of 10% to 20% more bandwidth wasted than TCP.
 
 
-Using kcp in frp:
+KCP mode uses UDP as the underlying transport. Using KCP in frp:
 
 
-1. Enable kcp protocol in frps:
+1. Enable KCP in frps:
 
 
   ```ini
   ```ini
   # frps.ini
   # frps.ini
   [common]
   [common]
   bind_port = 7000
   bind_port = 7000
-  # kcp needs to bind a udp port, it can be same with 'bind_port'
+  # Specify a UDP port for KCP.
   kcp_bind_port = 7000
   kcp_bind_port = 7000
   ```
   ```
 
 
-2. Configure the protocol used in frpc to connect frps:
+  The `kcp_bind_port` number can be the same number as `bind_port`, since `bind_port` field specifies a TCP port.
+
+2. Configure `frpc.ini` to use KCP to connect to frps:
 
 
   ```ini
   ```ini
   # frpc.ini
   # frpc.ini
   [common]
   [common]
   server_addr = x.x.x.x
   server_addr = x.x.x.x
-  # specify the 'kcp_bind_port' in frps
+  # Same as the 'kcp_bind_port' in frps.ini
   server_port = 7000
   server_port = 7000
   protocol = kcp
   protocol = kcp
   ```
   ```
 
 
-### Connection Pool
+### Connection Pooling
 
 
-By default, frps send message to frpc for create a new connection to backward service when getting an user request.If a proxy's connection pool is enabled, there will be a specified number of connections pre-established.
+By default, frps creates a new frpc connection to the backend service upon a user request. With connection pooling, frps keeps a certain number of pre-established connections, reducing the time needed to establish a connection.
 
 
-This feature is fit for a large number of short connections.
+This feature is suitable for a large number of short connections.
 
 
-1. Configure the limit of pool count each proxy can use in frps.ini:
+1. Configure the limit of pool count each proxy can use in `frps.ini`:
 
 
   ```ini
   ```ini
   # frps.ini
   # frps.ini
@@ -562,7 +562,7 @@ This feature is fit for a large number of short connections.
 
 
 Load balancing is supported by `group`.
 Load balancing is supported by `group`.
 
 
-This feature is available only for type `tcp` and `http` now.
+This feature is only available for types `tcp` and `http` now.
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
@@ -583,23 +583,19 @@ group_key = 123
 
 
 `group_key` is used for authentication.
 `group_key` is used for authentication.
 
 
-Proxies in same group will accept connections from port 80 randomly.
+Connections to port 80 will be dispatched to proxies in the same group randomly.
 
 
-For `tcp` type, `remote_port` in one group shoud be same.
+For type `tcp`, `remote_port` in the same group should be the same.
 
 
-For `http` type, `custom_domains, subdomain, locations` shoud be same.
+For type `http`, `custom_domains`, `subdomain`, `locations` should be the same.
 
 
-### Health Check
+### Service Health Check
 
 
 Health check feature can help you achieve high availability with load balancing.
 Health check feature can help you achieve high availability with load balancing.
 
 
-Add `health_check_type = {type}` to enable health check.
-
-**type** can be tcp or http.
+Add `health_check_type = tcp` or `health_check_type = http` to enable health check.
 
 
-Type tcp will dial the service port and type http will send a http rquest to service and require a 200 response.
-
-Type tcp configuration:
+With health check type **tcp**, the service port will be pinged (TCPing):
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
@@ -607,77 +603,81 @@ Type tcp configuration:
 type = tcp
 type = tcp
 local_port = 22
 local_port = 22
 remote_port = 6000
 remote_port = 6000
-# enable tcp health check
+# Enable TCP health check
 health_check_type = tcp
 health_check_type = tcp
-# dial timeout seconds
+# TCPing timeout seconds
 health_check_timeout_s = 3
 health_check_timeout_s = 3
-# if continuous failed in 3 times, the proxy will be removed from frps
+# If health check failed 3 times in a row, the proxy will be removed from frps
 health_check_max_failed = 3
 health_check_max_failed = 3
-# every 10 seconds will do a health check
+# A health check every 10 seconds
 health_check_interval_s = 10
 health_check_interval_s = 10
 ```
 ```
 
 
-Type http configuration:
+With health check type **http**, an HTTP request will be sent to the service and an HTTP 2xx OK response is expected:
+
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
 [web]
 [web]
 type = http
 type = http
 local_ip = 127.0.0.1
 local_ip = 127.0.0.1
 local_port = 80
 local_port = 80
-custom_domains = test.yourdomain.com
-# enable http health check
+custom_domains = test.example.com
+# Enable HTTP health check
 health_check_type = http
 health_check_type = http
-# frpc will send a GET http request '/status' to local http service
-# http service is alive when it return 2xx http response code
+# frpc will send a GET request to '/status'
+# and expect an HTTP 2xx OK response
 health_check_url = /status
 health_check_url = /status
-health_check_interval_s = 10
-health_check_max_failed = 3
 health_check_timeout_s = 3
 health_check_timeout_s = 3
+health_check_max_failed = 3
+health_check_interval_s = 10
 ```
 ```
 
 
-### Rewriting the Host Header
+### Rewriting the HTTP Host Header
 
 
-When forwarding to a local port, frp does not modify the tunneled HTTP requests at all, they are copied to your server byte-for-byte as they are received. Some application servers use the Host header for determining which development site to display. For this reason, frp can rewrite your requests with a modified host header. Use the `host_header_rewrite` switch to rewrite incoming HTTP requests.
+By default frp does not modify the tunneled HTTP requests at all as it's a byte-for-byte copy.
+
+However, speaking of web servers and HTTP requests, your web server might rely on the `Host` HTTP header to determine the website to be accessed. frp can rewrite the `Host` header when forwarding the HTTP requests, with the `host_header_rewrite` field:
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
 [web]
 [web]
 type = http
 type = http
 local_port = 80
 local_port = 80
-custom_domains = test.yourdomain.com
-host_header_rewrite = dev.yourdomain.com
+custom_domains = test.example.com
+host_header_rewrite = dev.example.com
 ```
 ```
 
 
-The `Host` request header will be rewritten to `Host: dev.yourdomain.com` before it reach your local http server.
+The HTTP request will have the the `Host` header rewritten to `Host: dev.example.com` when it reaches the actual web server, although the request from the browser probably has `Host: test.example.com`.
 
 
-### Set Headers In HTTP Request
+### Setting other HTTP Headers
 
 
-You can set headers for proxy which type is `http`.
+Similar to `Host`, You can override other HTTP request headers with proxy type `http`.
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
 [web]
 [web]
 type = http
 type = http
 local_port = 80
 local_port = 80
-custom_domains = test.yourdomain.com
-host_header_rewrite = dev.yourdomain.com
+custom_domains = test.example.com
+host_header_rewrite = dev.example.com
 header_X-From-Where = frp
 header_X-From-Where = frp
 ```
 ```
 
 
-Note that params which have prefix `header_` will be added to http request headers.
-In this example, it will set header `X-From-Where: frp` to http request.
+Note that parameter(s) prefixed with `header_` will be added to HTTP request headers.
+
+In this example, it will set header `X-From-Where: frp` in the HTTP request.
 
 
 ### Get Real IP
 ### Get Real IP
 
 
 #### HTTP X-Forwarded-For
 #### HTTP X-Forwarded-For
 
 
-Features for http proxy only.
+This feature is for http proxy only.
 
 
-You can get user's real IP from HTTP request header `X-Forwarded-For` and `X-Real-IP`.
+You can get user's real IP from HTTP request headers `X-Forwarded-For` and `X-Real-IP`.
 
 
 #### Proxy Protocol
 #### Proxy Protocol
 
 
-frp support Proxy Protocol to send user's real IP to local service. It support all types without UDP.
+frp supports Proxy Protocol to send user's real IP to local services. It support all types except UDP.
 
 
 Here is an example for https service:
 Here is an example for https service:
 
 
@@ -686,21 +686,19 @@ Here is an example for https service:
 [web]
 [web]
 type = https
 type = https
 local_port = 443
 local_port = 443
-custom_domains = test.yourdomain.com
+custom_domains = test.example.com
 
 
-# now v1 and v2 is supported
+# now v1 and v2 are supported
 proxy_protocol_version = v2
 proxy_protocol_version = v2
 ```
 ```
 
 
-You can enable Proxy Protocol support in nginx to parse user's real IP to http header `X-Real-IP`.
+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.
 
 
-Then you can get it from HTTP request header in your local service.
-
-### Password protecting your web service
+### 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.
 Anyone who can guess your tunnel URL can access your local web server unless you protect it with a password.
 
 
-This enforces HTTP Basic Auth on all requests with the username and password you specify in frpc's configure file.
+This enforces HTTP Basic Auth on all requests with the username and password specified in frpc's configure file.
 
 
 It can only be enabled when proxy type is http.
 It can only be enabled when proxy type is http.
 
 
@@ -709,23 +707,23 @@ It can only be enabled when proxy type is http.
 [web]
 [web]
 type = http
 type = http
 local_port = 80
 local_port = 80
-custom_domains = test.yourdomain.com
+custom_domains = test.example.com
 http_user = abc
 http_user = abc
 http_pwd = abc
 http_pwd = abc
 ```
 ```
 
 
-Visit `http://test.yourdomain.com` and now you need to input username and password.
+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、https type when many people use one frps server together.
+It is convenient to use `subdomain` configure for http and https types when many people share one frps server.
 
 
 ```ini
 ```ini
 # frps.ini
 # frps.ini
 subdomain_host = frps.com
 subdomain_host = frps.com
 ```
 ```
 
 
-Resolve `*.frps.com` to the frps server's IP.
+Resolve `*.frps.com` to the frps server's IP. This is usually called a Wildcard DNS record.
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
@@ -735,35 +733,36 @@ local_port = 80
 subdomain = test
 subdomain = test
 ```
 ```
 
 
-Now you can visit your web service by host `test.frps.com`.
+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`.
 Note that if `subdomain_host` is not empty, `custom_domains` should not be the subdomain of `subdomain_host`.
 
 
 ### URL routing
 ### URL routing
 
 
-frp support forward http requests to different backward web services by url routing.
+frp supports forwarding HTTP requests to different backend web services by url routing.
 
 
-`locations` specify the prefix of URL used for routing. frps first searches for the most specific prefix location given by literal strings regardless of the listed order.
+`locations` specifies the prefix of URL used for routing. frps first searches for the most specific prefix location given by literal strings regardless of the listed order.
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
 [web01]
 [web01]
 type = http
 type = http
 local_port = 80
 local_port = 80
-custom_domains = web.yourdomain.com
+custom_domains = web.example.com
 locations = /
 locations = /
 
 
 [web02]
 [web02]
 type = http
 type = http
 local_port = 81
 local_port = 81
-custom_domains = web.yourdomain.com
+custom_domains = web.example.com
 locations = /news,/about
 locations = /news,/about
 ```
 ```
-Http requests with url prefix `/news` and `/about` will be forwarded to **web02** and others to **web01**.
 
 
-### Connect frps by HTTP PROXY
+HTTP requests with URL prefix `/news` or `/about` will be forwarded to **web02** and other requests to **web01**.
+
+### Connecting to frps via HTTP PROXY
 
 
-frpc can connect frps using HTTP PROXY if you set os environment `HTTP_PROXY` or configure `http_proxy` param in frpc.ini file.
+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.
 
 
 It only works when protocol is tcp.
 It only works when protocol is tcp.
 
 
@@ -777,7 +776,7 @@ http_proxy = http://user:pwd@192.168.1.128:8080
 
 
 ### Range ports mapping
 ### Range ports mapping
 
 
-Proxy name has prefix `range:` will support mapping range ports.
+Proxy with names that start with `range:` will support mapping range ports.
 
 
 ```ini
 ```ini
 # frpc.ini
 # frpc.ini
@@ -788,15 +787,15 @@ local_port = 6000-6006,6007
 remote_port = 6000-6006,6007
 remote_port = 6000-6006,6007
 ```
 ```
 
 
-frpc will generate 8 proxies like `test_tcp_0, test_tcp_1 ... test_tcp_7`.
+frpc will generate 8 proxies like `test_tcp_0`, `test_tcp_1`, ..., `test_tcp_7`.
 
 
-### Plugin
+### Plugins
 
 
-frpc only forward request to local tcp or udp port by default.
+frpc only forwards requests to local TCP or UDP ports by default.
 
 
-Plugin is used for providing rich features. There are built-in plugins such as `unix_domain_socket`, `http_proxy`, `socks5`, `static_file` and you can see [example usage](#example-usage).
+Plugins are used for providing rich features. There are built-in plugins such as `unix_domain_socket`, `http_proxy`, `socks5`, `static_file` and you can see [example usage](#example-usage).
 
 
-Specify which plugin to use by `plugin` parameter. Configuration parameters of plugin should be started with `plugin_`. `local_ip` and `local_port` is useless for plugin.
+Specify which plugin to use with the `plugin` parameter. Configuration parameters of plugin should be started with `plugin_`. `local_ip` and `local_port` are not used for plugin.
 
 
 Using plugin **http_proxy**:
 Using plugin **http_proxy**:
 
 
@@ -814,7 +813,7 @@ plugin_http_passwd = abc
 
 
 ## Development Plan
 ## Development Plan
 
 
-* Log http request information in frps.
+* Log HTTP request information in frps.
 
 
 ## Contributing
 ## Contributing
 
 
@@ -822,14 +821,14 @@ Interested in getting involved? We would like to help you!
 
 
 * Take a look at our [issues list](https://github.com/fatedier/frp/issues) and consider sending a Pull Request to **dev branch**.
 * Take a look at our [issues list](https://github.com/fatedier/frp/issues) and consider sending a Pull Request to **dev branch**.
 * If you want to add a new feature, please create an issue first to describe the new feature, as well as the implementation approach. Once a proposal is accepted, create an implementation of the new features and submit it as a pull request.
 * If you want to add a new feature, please create an issue first to describe the new feature, as well as the implementation approach. Once a proposal is accepted, create an implementation of the new features and submit it as a pull request.
-* Sorry for my poor english and improvement for this document is welcome even some typo fix.
-* If you have some wonderful ideas, send email to fatedier@gmail.com.
+* Sorry for my poor English. Improvements for this document are welcome, even some typo fixes.
+* If you have great ideas, send an email to fatedier@gmail.com.
 
 
-**Note: We prefer you to give your advise in [issues](https://github.com/fatedier/frp/issues), so others with a same question can search it quickly and we don't need to answer them repeatly.**
+**Note: We prefer you to give your advise in [issues](https://github.com/fatedier/frp/issues), so others with a same question can search it quickly and we don't need to answer them repeatedly.**
 
 
 ## Donation
 ## Donation
 
 
-If frp help you a lot, you can support us by:
+If frp helps you a lot, you can support us by:
 
 
 frp QQ group: 606194980
 frp QQ group: 606194980
 
 

+ 44 - 29
client/control.go

@@ -15,9 +15,11 @@
 package client
 package client
 
 
 import (
 import (
+	"context"
 	"crypto/tls"
 	"crypto/tls"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"net"
 	"runtime/debug"
 	"runtime/debug"
 	"sync"
 	"sync"
 	"time"
 	"time"
@@ -25,8 +27,8 @@ import (
 	"github.com/fatedier/frp/client/proxy"
 	"github.com/fatedier/frp/client/proxy"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/msg"
-	"github.com/fatedier/frp/utils/log"
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	"github.com/fatedier/golib/control/shutdown"
 	"github.com/fatedier/golib/control/shutdown"
 	"github.com/fatedier/golib/crypto"
 	"github.com/fatedier/golib/crypto"
@@ -45,7 +47,7 @@ type Control struct {
 	vm *VisitorManager
 	vm *VisitorManager
 
 
 	// control connection
 	// control connection
-	conn frpNet.Conn
+	conn net.Conn
 
 
 	// tcp stream multiplexing, if enabled
 	// tcp stream multiplexing, if enabled
 	session *fmux.Session
 	session *fmux.Session
@@ -76,12 +78,19 @@ type Control struct {
 
 
 	mu sync.RWMutex
 	mu sync.RWMutex
 
 
-	log.Logger
+	xl *xlog.Logger
+
+	// service context
+	ctx context.Context
 }
 }
 
 
-func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, clientCfg config.ClientCommonConf,
-	pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, serverUDPPort int) *Control {
+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 {
 
 
+	// new xlog instance
 	ctl := &Control{
 	ctl := &Control{
 		runId:              runId,
 		runId:              runId,
 		conn:               conn,
 		conn:               conn,
@@ -96,11 +105,12 @@ func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, clientCfg
 		writerShutdown:     shutdown.New(),
 		writerShutdown:     shutdown.New(),
 		msgHandlerShutdown: shutdown.New(),
 		msgHandlerShutdown: shutdown.New(),
 		serverUDPPort:      serverUDPPort,
 		serverUDPPort:      serverUDPPort,
-		Logger:             log.NewPrefixLogger(""),
+		xl:                 xlog.FromContextSafe(ctx),
+		ctx:                ctx,
 	}
 	}
-	ctl.pm = proxy.NewProxyManager(ctl.sendCh, runId, clientCfg, serverUDPPort)
+	ctl.pm = proxy.NewProxyManager(ctl.ctx, ctl.sendCh, clientCfg, serverUDPPort)
 
 
-	ctl.vm = NewVisitorManager(ctl)
+	ctl.vm = NewVisitorManager(ctl.ctx, ctl)
 	ctl.vm.Reload(visitorCfgs)
 	ctl.vm.Reload(visitorCfgs)
 	return ctl
 	return ctl
 }
 }
@@ -117,6 +127,7 @@ func (ctl *Control) Run() {
 }
 }
 
 
 func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
 func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
+	xl := ctl.xl
 	workConn, err := ctl.connectServer()
 	workConn, err := ctl.connectServer()
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -126,31 +137,31 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
 		RunId: ctl.runId,
 		RunId: ctl.runId,
 	}
 	}
 	if err = msg.WriteMsg(workConn, m); err != nil {
 	if err = msg.WriteMsg(workConn, m); err != nil {
-		ctl.Warn("work connection write to server error: %v", err)
+		xl.Warn("work connection write to server error: %v", err)
 		workConn.Close()
 		workConn.Close()
 		return
 		return
 	}
 	}
 
 
 	var startMsg msg.StartWorkConn
 	var startMsg msg.StartWorkConn
 	if err = msg.ReadMsgInto(workConn, &startMsg); err != nil {
 	if err = msg.ReadMsgInto(workConn, &startMsg); err != nil {
-		ctl.Error("work connection closed, %v", err)
+		xl.Error("work connection closed before response StartWorkConn message: %v", err)
 		workConn.Close()
 		workConn.Close()
 		return
 		return
 	}
 	}
-	workConn.AddLogPrefix(startMsg.ProxyName)
 
 
 	// dispatch this work connection to related proxy
 	// dispatch this work connection to related proxy
 	ctl.pm.HandleWorkConn(startMsg.ProxyName, workConn, &startMsg)
 	ctl.pm.HandleWorkConn(startMsg.ProxyName, workConn, &startMsg)
 }
 }
 
 
 func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) {
 func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) {
+	xl := ctl.xl
 	// Server will return NewProxyResp message to each NewProxy message.
 	// Server will return NewProxyResp message to each NewProxy message.
 	// Start a new proxy handler if no error got
 	// Start a new proxy handler if no error got
 	err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error)
 	err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error)
 	if err != nil {
 	if err != nil {
-		ctl.Warn("[%s] start error: %v", inMsg.ProxyName, err)
+		xl.Warn("[%s] start error: %v", inMsg.ProxyName, err)
 	} else {
 	} else {
-		ctl.Info("[%s] start proxy success", inMsg.ProxyName)
+		xl.Info("[%s] start proxy success", inMsg.ProxyName)
 	}
 	}
 }
 }
 
 
@@ -169,15 +180,16 @@ func (ctl *Control) ClosedDoneCh() <-chan struct{} {
 }
 }
 
 
 // connectServer return a new connection to frps
 // connectServer return a new connection to frps
-func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
+func (ctl *Control) connectServer() (conn net.Conn, err error) {
+	xl := ctl.xl
 	if ctl.clientCfg.TcpMux {
 	if ctl.clientCfg.TcpMux {
 		stream, errRet := ctl.session.OpenStream()
 		stream, errRet := ctl.session.OpenStream()
 		if errRet != nil {
 		if errRet != nil {
 			err = errRet
 			err = errRet
-			ctl.Warn("start new connection to server error: %v", err)
+			xl.Warn("start new connection to server error: %v", err)
 			return
 			return
 		}
 		}
-		conn = frpNet.WrapConn(stream)
+		conn = stream
 	} else {
 	} else {
 		var tlsConfig *tls.Config
 		var tlsConfig *tls.Config
 		if ctl.clientCfg.TLSEnable {
 		if ctl.clientCfg.TLSEnable {
@@ -188,7 +200,7 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
 		conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HttpProxy, ctl.clientCfg.Protocol,
 		conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HttpProxy, ctl.clientCfg.Protocol,
 			fmt.Sprintf("%s:%d", ctl.clientCfg.ServerAddr, ctl.clientCfg.ServerPort), tlsConfig)
 			fmt.Sprintf("%s:%d", ctl.clientCfg.ServerAddr, ctl.clientCfg.ServerPort), tlsConfig)
 		if err != nil {
 		if err != nil {
-			ctl.Warn("start new connection to server error: %v", err)
+			xl.Warn("start new connection to server error: %v", err)
 			return
 			return
 		}
 		}
 	}
 	}
@@ -197,10 +209,11 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
 
 
 // reader read all messages from frps and send to readCh
 // reader read all messages from frps and send to readCh
 func (ctl *Control) reader() {
 func (ctl *Control) reader() {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.Error("panic error: %v", err)
-			ctl.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 	defer ctl.readerShutdown.Done()
 	defer ctl.readerShutdown.Done()
@@ -210,10 +223,10 @@ func (ctl *Control) reader() {
 	for {
 	for {
 		if m, err := msg.ReadMsg(encReader); err != nil {
 		if m, err := msg.ReadMsg(encReader); err != nil {
 			if err == io.EOF {
 			if err == io.EOF {
-				ctl.Debug("read from control connection EOF")
+				xl.Debug("read from control connection EOF")
 				return
 				return
 			} else {
 			} else {
-				ctl.Warn("read error: %v", err)
+				xl.Warn("read error: %v", err)
 				ctl.conn.Close()
 				ctl.conn.Close()
 				return
 				return
 			}
 			}
@@ -225,20 +238,21 @@ func (ctl *Control) reader() {
 
 
 // writer writes messages got from sendCh to frps
 // writer writes messages got from sendCh to frps
 func (ctl *Control) writer() {
 func (ctl *Control) writer() {
+	xl := ctl.xl
 	defer ctl.writerShutdown.Done()
 	defer ctl.writerShutdown.Done()
 	encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token))
 	encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token))
 	if err != nil {
 	if err != nil {
-		ctl.conn.Error("crypto new writer error: %v", err)
+		xl.Error("crypto new writer error: %v", err)
 		ctl.conn.Close()
 		ctl.conn.Close()
 		return
 		return
 	}
 	}
 	for {
 	for {
 		if m, ok := <-ctl.sendCh; !ok {
 		if m, ok := <-ctl.sendCh; !ok {
-			ctl.Info("control writer is closing")
+			xl.Info("control writer is closing")
 			return
 			return
 		} else {
 		} else {
 			if err := msg.WriteMsg(encWriter, m); err != nil {
 			if err := msg.WriteMsg(encWriter, m); err != nil {
-				ctl.Warn("write message to control connection error: %v", err)
+				xl.Warn("write message to control connection error: %v", err)
 				return
 				return
 			}
 			}
 		}
 		}
@@ -247,10 +261,11 @@ func (ctl *Control) writer() {
 
 
 // msgHandler handles all channel events and do corresponding operations.
 // msgHandler handles all channel events and do corresponding operations.
 func (ctl *Control) msgHandler() {
 func (ctl *Control) msgHandler() {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.Error("panic error: %v", err)
-			ctl.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 	defer ctl.msgHandlerShutdown.Done()
 	defer ctl.msgHandlerShutdown.Done()
@@ -266,11 +281,11 @@ func (ctl *Control) msgHandler() {
 		select {
 		select {
 		case <-hbSend.C:
 		case <-hbSend.C:
 			// send heartbeat to server
 			// send heartbeat to server
-			ctl.Debug("send heartbeat to server")
+			xl.Debug("send heartbeat to server")
 			ctl.sendCh <- &msg.Ping{}
 			ctl.sendCh <- &msg.Ping{}
 		case <-hbCheck.C:
 		case <-hbCheck.C:
 			if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartBeatTimeout)*time.Second {
 			if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartBeatTimeout)*time.Second {
-				ctl.Warn("heartbeat timeout")
+				xl.Warn("heartbeat timeout")
 				// let reader() stop
 				// let reader() stop
 				ctl.conn.Close()
 				ctl.conn.Close()
 				return
 				return
@@ -287,7 +302,7 @@ func (ctl *Control) msgHandler() {
 				ctl.HandleNewProxyResp(m)
 				ctl.HandleNewProxyResp(m)
 			case *msg.Pong:
 			case *msg.Pong:
 				ctl.lastPong = time.Now()
 				ctl.lastPong = time.Now()
-				ctl.Debug("receive heartbeat from server")
+				xl.Debug("receive heartbeat from server")
 			}
 			}
 		}
 		}
 	}
 	}

+ 11 - 22
client/health/health.go

@@ -24,7 +24,7 @@ import (
 	"net/http"
 	"net/http"
 	"time"
 	"time"
 
 
-	"github.com/fatedier/frp/utils/log"
+	"github.com/fatedier/frp/utils/xlog"
 )
 )
 
 
 var (
 var (
@@ -50,11 +50,11 @@ type HealthCheckMonitor struct {
 
 
 	ctx    context.Context
 	ctx    context.Context
 	cancel context.CancelFunc
 	cancel context.CancelFunc
-
-	l log.Logger
 }
 }
 
 
-func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string,
+func NewHealthCheckMonitor(ctx context.Context, checkType string,
+	intervalS int, timeoutS int, maxFailedTimes int,
+	addr string, url string,
 	statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor {
 	statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor {
 
 
 	if intervalS <= 0 {
 	if intervalS <= 0 {
@@ -66,7 +66,7 @@ func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFai
 	if maxFailedTimes <= 0 {
 	if maxFailedTimes <= 0 {
 		maxFailedTimes = 1
 		maxFailedTimes = 1
 	}
 	}
-	ctx, cancel := context.WithCancel(context.Background())
+	newctx, cancel := context.WithCancel(ctx)
 	return &HealthCheckMonitor{
 	return &HealthCheckMonitor{
 		checkType:      checkType,
 		checkType:      checkType,
 		interval:       time.Duration(intervalS) * time.Second,
 		interval:       time.Duration(intervalS) * time.Second,
@@ -77,15 +77,11 @@ func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFai
 		statusOK:       false,
 		statusOK:       false,
 		statusNormalFn: statusNormalFn,
 		statusNormalFn: statusNormalFn,
 		statusFailedFn: statusFailedFn,
 		statusFailedFn: statusFailedFn,
-		ctx:            ctx,
+		ctx:            newctx,
 		cancel:         cancel,
 		cancel:         cancel,
 	}
 	}
 }
 }
 
 
-func (monitor *HealthCheckMonitor) SetLogger(l log.Logger) {
-	monitor.l = l
-}
-
 func (monitor *HealthCheckMonitor) Start() {
 func (monitor *HealthCheckMonitor) Start() {
 	go monitor.checkWorker()
 	go monitor.checkWorker()
 }
 }
@@ -95,6 +91,7 @@ func (monitor *HealthCheckMonitor) Stop() {
 }
 }
 
 
 func (monitor *HealthCheckMonitor) checkWorker() {
 func (monitor *HealthCheckMonitor) checkWorker() {
+	xl := xlog.FromContextSafe(monitor.ctx)
 	for {
 	for {
 		doCtx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout))
 		doCtx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout))
 		err := monitor.doCheck(doCtx)
 		err := monitor.doCheck(doCtx)
@@ -109,25 +106,17 @@ func (monitor *HealthCheckMonitor) checkWorker() {
 		}
 		}
 
 
 		if err == nil {
 		if err == nil {
-			if monitor.l != nil {
-				monitor.l.Trace("do one health check success")
-			}
+			xl.Trace("do one health check success")
 			if !monitor.statusOK && monitor.statusNormalFn != nil {
 			if !monitor.statusOK && monitor.statusNormalFn != nil {
-				if monitor.l != nil {
-					monitor.l.Info("health check status change to success")
-				}
+				xl.Info("health check status change to success")
 				monitor.statusOK = true
 				monitor.statusOK = true
 				monitor.statusNormalFn()
 				monitor.statusNormalFn()
 			}
 			}
 		} else {
 		} else {
-			if monitor.l != nil {
-				monitor.l.Warn("do one health check failed: %v", err)
-			}
+			xl.Warn("do one health check failed: %v", err)
 			monitor.failedTimes++
 			monitor.failedTimes++
 			if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
 			if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
-				if monitor.l != nil {
-					monitor.l.Warn("health check status change to failed")
-				}
+				xl.Warn("health check status change to failed")
 				monitor.statusOK = false
 				monitor.statusOK = false
 				monitor.statusFailedFn()
 				monitor.statusFailedFn()
 			}
 			}

+ 61 - 54
client/proxy/proxy.go

@@ -16,6 +16,7 @@ package proxy
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"context"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -29,8 +30,8 @@ import (
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/plugin"
 	"github.com/fatedier/frp/models/plugin"
 	"github.com/fatedier/frp/models/proto/udp"
 	"github.com/fatedier/frp/models/proto/udp"
-	"github.com/fatedier/frp/utils/log"
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	"github.com/fatedier/golib/errors"
 	"github.com/fatedier/golib/errors"
 	frpIo "github.com/fatedier/golib/io"
 	frpIo "github.com/fatedier/golib/io"
@@ -44,17 +45,17 @@ type Proxy interface {
 	Run() error
 	Run() error
 
 
 	// InWorkConn accept work connections registered to server.
 	// InWorkConn accept work connections registered to server.
-	InWorkConn(frpNet.Conn, *msg.StartWorkConn)
+	InWorkConn(net.Conn, *msg.StartWorkConn)
 
 
 	Close()
 	Close()
-	log.Logger
 }
 }
 
 
-func NewProxy(pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serverUDPPort int) (pxy Proxy) {
+func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serverUDPPort int) (pxy Proxy) {
 	baseProxy := BaseProxy{
 	baseProxy := BaseProxy{
-		Logger:        log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
 		clientCfg:     clientCfg,
 		clientCfg:     clientCfg,
 		serverUDPPort: serverUDPPort,
 		serverUDPPort: serverUDPPort,
+		xl:            xlog.FromContextSafe(ctx),
+		ctx:           ctx,
 	}
 	}
 	switch cfg := pxyConf.(type) {
 	switch cfg := pxyConf.(type) {
 	case *config.TcpProxyConf:
 	case *config.TcpProxyConf:
@@ -93,10 +94,12 @@ func NewProxy(pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serve
 
 
 type BaseProxy struct {
 type BaseProxy struct {
 	closed        bool
 	closed        bool
-	mu            sync.RWMutex
 	clientCfg     config.ClientCommonConf
 	clientCfg     config.ClientCommonConf
 	serverUDPPort int
 	serverUDPPort int
-	log.Logger
+
+	mu  sync.RWMutex
+	xl  *xlog.Logger
+	ctx context.Context
 }
 }
 
 
 // TCP
 // TCP
@@ -123,8 +126,8 @@ func (pxy *TcpProxy) Close() {
 	}
 	}
 }
 }
 
 
-func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
-	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
+func (pxy *TcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
+	HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
 		[]byte(pxy.clientCfg.Token), m)
 		[]byte(pxy.clientCfg.Token), m)
 }
 }
 
 
@@ -152,8 +155,8 @@ func (pxy *HttpProxy) Close() {
 	}
 	}
 }
 }
 
 
-func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
-	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
+func (pxy *HttpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
+	HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
 		[]byte(pxy.clientCfg.Token), m)
 		[]byte(pxy.clientCfg.Token), m)
 }
 }
 
 
@@ -181,8 +184,8 @@ func (pxy *HttpsProxy) Close() {
 	}
 	}
 }
 }
 
 
-func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
-	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
+func (pxy *HttpsProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
+	HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
 		[]byte(pxy.clientCfg.Token), m)
 		[]byte(pxy.clientCfg.Token), m)
 }
 }
 
 
@@ -210,8 +213,8 @@ func (pxy *StcpProxy) Close() {
 	}
 	}
 }
 }
 
 
-func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
-	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
+func (pxy *StcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
+	HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
 		[]byte(pxy.clientCfg.Token), m)
 		[]byte(pxy.clientCfg.Token), m)
 }
 }
 
 
@@ -239,12 +242,13 @@ func (pxy *XtcpProxy) Close() {
 	}
 	}
 }
 }
 
 
-func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
+func (pxy *XtcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
+	xl := pxy.xl
 	defer conn.Close()
 	defer conn.Close()
 	var natHoleSidMsg msg.NatHoleSid
 	var natHoleSidMsg msg.NatHoleSid
 	err := msg.ReadMsgInto(conn, &natHoleSidMsg)
 	err := msg.ReadMsgInto(conn, &natHoleSidMsg)
 	if err != nil {
 	if err != nil {
-		pxy.Error("xtcp read from workConn error: %v", err)
+		xl.Error("xtcp read from workConn error: %v", err)
 		return
 		return
 	}
 	}
 
 
@@ -259,7 +263,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 
 
 	err = msg.WriteMsg(clientConn, natHoleClientMsg)
 	err = msg.WriteMsg(clientConn, natHoleClientMsg)
 	if err != nil {
 	if err != nil {
-		pxy.Error("send natHoleClientMsg to server error: %v", err)
+		xl.Error("send natHoleClientMsg to server error: %v", err)
 		return
 		return
 	}
 	}
 
 
@@ -270,28 +274,28 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 	buf := pool.GetBuf(1024)
 	buf := pool.GetBuf(1024)
 	n, err := clientConn.Read(buf)
 	n, err := clientConn.Read(buf)
 	if err != nil {
 	if err != nil {
-		pxy.Error("get natHoleRespMsg error: %v", err)
+		xl.Error("get natHoleRespMsg error: %v", err)
 		return
 		return
 	}
 	}
 	err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
 	err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
 	if err != nil {
 	if err != nil {
-		pxy.Error("get natHoleRespMsg error: %v", err)
+		xl.Error("get natHoleRespMsg error: %v", err)
 		return
 		return
 	}
 	}
 	clientConn.SetReadDeadline(time.Time{})
 	clientConn.SetReadDeadline(time.Time{})
 	clientConn.Close()
 	clientConn.Close()
 
 
 	if natHoleRespMsg.Error != "" {
 	if natHoleRespMsg.Error != "" {
-		pxy.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
+		xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
 		return
 		return
 	}
 	}
 
 
-	pxy.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
+	xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
 
 
 	// Send detect message
 	// Send detect message
 	array := strings.Split(natHoleRespMsg.VisitorAddr, ":")
 	array := strings.Split(natHoleRespMsg.VisitorAddr, ":")
 	if len(array) <= 1 {
 	if len(array) <= 1 {
-		pxy.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
+		xl.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
 	}
 	}
 	laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
 	laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
 	/*
 	/*
@@ -301,18 +305,18 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 	*/
 	*/
 	port, err := strconv.ParseInt(array[1], 10, 64)
 	port, err := strconv.ParseInt(array[1], 10, 64)
 	if err != nil {
 	if err != nil {
-		pxy.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
+		xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
 		return
 		return
 	}
 	}
 	pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
 	pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
-	pxy.Trace("send all detect msg done")
+	xl.Trace("send all detect msg done")
 
 
 	msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
 	msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
 
 
 	// Listen for clientConn's address and wait for visitor connection
 	// Listen for clientConn's address and wait for visitor connection
 	lConn, err := net.ListenUDP("udp", laddr)
 	lConn, err := net.ListenUDP("udp", laddr)
 	if err != nil {
 	if err != nil {
-		pxy.Error("listen on visitorConn's local adress error: %v", err)
+		xl.Error("listen on visitorConn's local adress error: %v", err)
 		return
 		return
 	}
 	}
 	defer lConn.Close()
 	defer lConn.Close()
@@ -322,22 +326,22 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 	var uAddr *net.UDPAddr
 	var uAddr *net.UDPAddr
 	n, uAddr, err = lConn.ReadFromUDP(sidBuf)
 	n, uAddr, err = lConn.ReadFromUDP(sidBuf)
 	if err != nil {
 	if err != nil {
-		pxy.Warn("get sid from visitor error: %v", err)
+		xl.Warn("get sid from visitor error: %v", err)
 		return
 		return
 	}
 	}
 	lConn.SetReadDeadline(time.Time{})
 	lConn.SetReadDeadline(time.Time{})
 	if string(sidBuf[:n]) != natHoleRespMsg.Sid {
 	if string(sidBuf[:n]) != natHoleRespMsg.Sid {
-		pxy.Warn("incorrect sid from visitor")
+		xl.Warn("incorrect sid from visitor")
 		return
 		return
 	}
 	}
 	pool.PutBuf(sidBuf)
 	pool.PutBuf(sidBuf)
-	pxy.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
+	xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
 
 
 	lConn.WriteToUDP(sidBuf[:n], uAddr)
 	lConn.WriteToUDP(sidBuf[:n], uAddr)
 
 
 	kcpConn, err := frpNet.NewKcpConnFromUdp(lConn, false, natHoleRespMsg.VisitorAddr)
 	kcpConn, err := frpNet.NewKcpConnFromUdp(lConn, false, natHoleRespMsg.VisitorAddr)
 	if err != nil {
 	if err != nil {
-		pxy.Error("create kcp connection from udp connection error: %v", err)
+		xl.Error("create kcp connection from udp connection error: %v", err)
 		return
 		return
 	}
 	}
 
 
@@ -346,18 +350,18 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 	fmuxCfg.LogOutput = ioutil.Discard
 	fmuxCfg.LogOutput = ioutil.Discard
 	sess, err := fmux.Server(kcpConn, fmuxCfg)
 	sess, err := fmux.Server(kcpConn, fmuxCfg)
 	if err != nil {
 	if err != nil {
-		pxy.Error("create yamux server from kcp connection error: %v", err)
+		xl.Error("create yamux server from kcp connection error: %v", err)
 		return
 		return
 	}
 	}
 	defer sess.Close()
 	defer sess.Close()
 	muxConn, err := sess.Accept()
 	muxConn, err := sess.Accept()
 	if err != nil {
 	if err != nil {
-		pxy.Error("accept for yamux connection error: %v", err)
+		xl.Error("accept for yamux connection error: %v", err)
 		return
 		return
 	}
 	}
 
 
-	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf,
-		frpNet.WrapConn(muxConn), []byte(pxy.cfg.Sk), m)
+	HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf,
+		muxConn, []byte(pxy.cfg.Sk), m)
 }
 }
 
 
 func (pxy *XtcpProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
 func (pxy *XtcpProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
@@ -390,7 +394,7 @@ type UdpProxy struct {
 
 
 	// include msg.UdpPacket and msg.Ping
 	// include msg.UdpPacket and msg.Ping
 	sendCh   chan msg.Message
 	sendCh   chan msg.Message
-	workConn frpNet.Conn
+	workConn net.Conn
 }
 }
 
 
 func (pxy *UdpProxy) Run() (err error) {
 func (pxy *UdpProxy) Run() (err error) {
@@ -419,8 +423,9 @@ func (pxy *UdpProxy) Close() {
 	}
 	}
 }
 }
 
 
-func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
-	pxy.Info("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
+func (pxy *UdpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
+	xl := pxy.xl
+	xl.Info("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
 	// close resources releated with old workConn
 	// close resources releated with old workConn
 	pxy.Close()
 	pxy.Close()
 
 
@@ -435,32 +440,32 @@ func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 		for {
 		for {
 			var udpMsg msg.UdpPacket
 			var udpMsg msg.UdpPacket
 			if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
 			if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
-				pxy.Warn("read from workConn for udp error: %v", errRet)
+				xl.Warn("read from workConn for udp error: %v", errRet)
 				return
 				return
 			}
 			}
 			if errRet := errors.PanicToError(func() {
 			if errRet := errors.PanicToError(func() {
-				pxy.Trace("get udp package from workConn: %s", udpMsg.Content)
+				xl.Trace("get udp package from workConn: %s", udpMsg.Content)
 				readCh <- &udpMsg
 				readCh <- &udpMsg
 			}); errRet != nil {
 			}); errRet != nil {
-				pxy.Info("reader goroutine for udp work connection closed: %v", errRet)
+				xl.Info("reader goroutine for udp work connection closed: %v", errRet)
 				return
 				return
 			}
 			}
 		}
 		}
 	}
 	}
 	workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
 	workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
 		defer func() {
 		defer func() {
-			pxy.Info("writer goroutine for udp work connection closed")
+			xl.Info("writer goroutine for udp work connection closed")
 		}()
 		}()
 		var errRet error
 		var errRet error
 		for rawMsg := range sendCh {
 		for rawMsg := range sendCh {
 			switch m := rawMsg.(type) {
 			switch m := rawMsg.(type) {
 			case *msg.UdpPacket:
 			case *msg.UdpPacket:
-				pxy.Trace("send udp package to workConn: %s", m.Content)
+				xl.Trace("send udp package to workConn: %s", m.Content)
 			case *msg.Ping:
 			case *msg.Ping:
-				pxy.Trace("send ping message to udp workConn")
+				xl.Trace("send ping message to udp workConn")
 			}
 			}
 			if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
 			if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
-				pxy.Error("udp work write error: %v", errRet)
+				xl.Error("udp work write error: %v", errRet)
 				return
 				return
 			}
 			}
 		}
 		}
@@ -472,7 +477,7 @@ func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 			if errRet = errors.PanicToError(func() {
 			if errRet = errors.PanicToError(func() {
 				sendCh <- &msg.Ping{}
 				sendCh <- &msg.Ping{}
 			}); errRet != nil {
 			}); errRet != nil {
-				pxy.Trace("heartbeat goroutine for udp work connection closed")
+				xl.Trace("heartbeat goroutine for udp work connection closed")
 				break
 				break
 			}
 			}
 		}
 		}
@@ -485,20 +490,22 @@ func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
 }
 }
 
 
 // Common handler for tcp work connections.
 // Common handler for tcp work connections.
-func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
-	baseInfo *config.BaseProxyConf, workConn frpNet.Conn, encKey []byte, m *msg.StartWorkConn) {
-
+func HandleTcpWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
+	baseInfo *config.BaseProxyConf, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) {
+	xl := xlog.FromContextSafe(ctx)
 	var (
 	var (
 		remote io.ReadWriteCloser
 		remote io.ReadWriteCloser
 		err    error
 		err    error
 	)
 	)
 	remote = workConn
 	remote = workConn
 
 
+	xl.Trace("handle tcp work connection, use_encryption: %t, use_compression: %t",
+		baseInfo.UseEncryption, baseInfo.UseCompression)
 	if baseInfo.UseEncryption {
 	if baseInfo.UseEncryption {
 		remote, err = frpIo.WithEncryption(remote, encKey)
 		remote, err = frpIo.WithEncryption(remote, encKey)
 		if err != nil {
 		if err != nil {
 			workConn.Close()
 			workConn.Close()
-			workConn.Error("create encryption stream error: %v", err)
+			xl.Error("create encryption stream error: %v", err)
 			return
 			return
 		}
 		}
 	}
 	}
@@ -541,19 +548,19 @@ func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, proxyPlugin plugin.
 
 
 	if proxyPlugin != nil {
 	if proxyPlugin != nil {
 		// if plugin is set, let plugin handle connections first
 		// if plugin is set, let plugin handle connections first
-		workConn.Debug("handle by plugin: %s", proxyPlugin.Name())
+		xl.Debug("handle by plugin: %s", proxyPlugin.Name())
 		proxyPlugin.Handle(remote, workConn, extraInfo)
 		proxyPlugin.Handle(remote, workConn, extraInfo)
-		workConn.Debug("handle by plugin finished")
+		xl.Debug("handle by plugin finished")
 		return
 		return
 	} else {
 	} else {
 		localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIp, localInfo.LocalPort))
 		localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIp, localInfo.LocalPort))
 		if err != nil {
 		if err != nil {
 			workConn.Close()
 			workConn.Close()
-			workConn.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIp, localInfo.LocalPort, err)
+			xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIp, localInfo.LocalPort, err)
 			return
 			return
 		}
 		}
 
 
-		workConn.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
+		xl.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
 			localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String())
 			localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String())
 
 
 		if len(extraInfo) > 0 {
 		if len(extraInfo) > 0 {
@@ -561,6 +568,6 @@ func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, proxyPlugin plugin.
 		}
 		}
 
 
 		frpIo.Join(localConn, remote)
 		frpIo.Join(localConn, remote)
-		workConn.Debug("join connections closed")
+		xl.Debug("join connections closed")
 	}
 	}
 }
 }

+ 12 - 12
client/proxy/proxy_manager.go

@@ -1,14 +1,15 @@
 package proxy
 package proxy
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
+	"net"
 	"sync"
 	"sync"
 
 
 	"github.com/fatedier/frp/client/event"
 	"github.com/fatedier/frp/client/event"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/msg"
-	"github.com/fatedier/frp/utils/log"
-	frpNet "github.com/fatedier/frp/utils/net"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	"github.com/fatedier/golib/errors"
 	"github.com/fatedier/golib/errors"
 )
 )
@@ -25,19 +26,17 @@ type ProxyManager struct {
 	// The UDP port that the server is listening on
 	// The UDP port that the server is listening on
 	serverUDPPort int
 	serverUDPPort int
 
 
-	logPrefix string
-	log.Logger
+	ctx context.Context
 }
 }
 
 
-func NewProxyManager(msgSendCh chan (msg.Message), logPrefix string, clientCfg config.ClientCommonConf, serverUDPPort int) *ProxyManager {
+func NewProxyManager(ctx context.Context, msgSendCh chan (msg.Message), clientCfg config.ClientCommonConf, serverUDPPort int) *ProxyManager {
 	return &ProxyManager{
 	return &ProxyManager{
-		proxies:       make(map[string]*ProxyWrapper),
 		sendCh:        msgSendCh,
 		sendCh:        msgSendCh,
+		proxies:       make(map[string]*ProxyWrapper),
 		closed:        false,
 		closed:        false,
 		clientCfg:     clientCfg,
 		clientCfg:     clientCfg,
 		serverUDPPort: serverUDPPort,
 		serverUDPPort: serverUDPPort,
-		logPrefix:     logPrefix,
-		Logger:        log.NewPrefixLogger(logPrefix),
+		ctx:           ctx,
 	}
 	}
 }
 }
 
 
@@ -65,7 +64,7 @@ func (pm *ProxyManager) Close() {
 	pm.proxies = make(map[string]*ProxyWrapper)
 	pm.proxies = make(map[string]*ProxyWrapper)
 }
 }
 
 
-func (pm *ProxyManager) HandleWorkConn(name string, workConn frpNet.Conn, m *msg.StartWorkConn) {
+func (pm *ProxyManager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWorkConn) {
 	pm.mu.RLock()
 	pm.mu.RLock()
 	pw, ok := pm.proxies[name]
 	pw, ok := pm.proxies[name]
 	pm.mu.RUnlock()
 	pm.mu.RUnlock()
@@ -104,6 +103,7 @@ func (pm *ProxyManager) GetAllProxyStatus() []*ProxyStatus {
 }
 }
 
 
 func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
 func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
+	xl := xlog.FromContextSafe(pm.ctx)
 	pm.mu.Lock()
 	pm.mu.Lock()
 	defer pm.mu.Unlock()
 	defer pm.mu.Unlock()
 
 
@@ -127,13 +127,13 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
 		}
 		}
 	}
 	}
 	if len(delPxyNames) > 0 {
 	if len(delPxyNames) > 0 {
-		pm.Info("proxy removed: %v", delPxyNames)
+		xl.Info("proxy removed: %v", delPxyNames)
 	}
 	}
 
 
 	addPxyNames := make([]string, 0)
 	addPxyNames := make([]string, 0)
 	for name, cfg := range pxyCfgs {
 	for name, cfg := range pxyCfgs {
 		if _, ok := pm.proxies[name]; !ok {
 		if _, ok := pm.proxies[name]; !ok {
-			pxy := NewProxyWrapper(cfg, pm.clientCfg, pm.HandleEvent, pm.logPrefix, pm.serverUDPPort)
+			pxy := NewProxyWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.serverUDPPort)
 			pm.proxies[name] = pxy
 			pm.proxies[name] = pxy
 			addPxyNames = append(addPxyNames, name)
 			addPxyNames = append(addPxyNames, name)
 
 
@@ -141,6 +141,6 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
 		}
 		}
 	}
 	}
 	if len(addPxyNames) > 0 {
 	if len(addPxyNames) > 0 {
-		pm.Info("proxy added: %v", addPxyNames)
+		xl.Info("proxy added: %v", addPxyNames)
 	}
 	}
 }
 }

+ 22 - 16
client/proxy/proxy_wrapper.go

@@ -1,7 +1,9 @@
 package proxy
 package proxy
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
+	"net"
 	"sync"
 	"sync"
 	"sync/atomic"
 	"sync/atomic"
 	"time"
 	"time"
@@ -10,8 +12,7 @@ import (
 	"github.com/fatedier/frp/client/health"
 	"github.com/fatedier/frp/client/health"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/msg"
-	"github.com/fatedier/frp/utils/log"
-	frpNet "github.com/fatedier/frp/utils/net"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	"github.com/fatedier/golib/errors"
 	"github.com/fatedier/golib/errors"
 )
 )
@@ -62,11 +63,13 @@ type ProxyWrapper struct {
 	healthNotifyCh   chan struct{}
 	healthNotifyCh   chan struct{}
 	mu               sync.RWMutex
 	mu               sync.RWMutex
 
 
-	log.Logger
+	xl  *xlog.Logger
+	ctx context.Context
 }
 }
 
 
-func NewProxyWrapper(cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.EventHandler, logPrefix string, serverUDPPort int) *ProxyWrapper {
+func NewProxyWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.EventHandler, serverUDPPort int) *ProxyWrapper {
 	baseInfo := cfg.GetBaseInfo()
 	baseInfo := cfg.GetBaseInfo()
+	xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.ProxyName)
 	pw := &ProxyWrapper{
 	pw := &ProxyWrapper{
 		ProxyStatus: ProxyStatus{
 		ProxyStatus: ProxyStatus{
 			Name:   baseInfo.ProxyName,
 			Name:   baseInfo.ProxyName,
@@ -77,20 +80,19 @@ func NewProxyWrapper(cfg config.ProxyConf, clientCfg config.ClientCommonConf, ev
 		closeCh:        make(chan struct{}),
 		closeCh:        make(chan struct{}),
 		healthNotifyCh: make(chan struct{}),
 		healthNotifyCh: make(chan struct{}),
 		handler:        eventHandler,
 		handler:        eventHandler,
-		Logger:         log.NewPrefixLogger(logPrefix),
+		xl:             xl,
+		ctx:            xlog.NewContext(ctx, xl),
 	}
 	}
-	pw.AddLogPrefix(pw.Name)
 
 
 	if baseInfo.HealthCheckType != "" {
 	if baseInfo.HealthCheckType != "" {
 		pw.health = 1 // means failed
 		pw.health = 1 // means failed
-		pw.monitor = health.NewHealthCheckMonitor(baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS,
+		pw.monitor = health.NewHealthCheckMonitor(pw.ctx, baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS,
 			baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr,
 			baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr,
 			baseInfo.HealthCheckUrl, pw.statusNormalCallback, pw.statusFailedCallback)
 			baseInfo.HealthCheckUrl, pw.statusNormalCallback, pw.statusFailedCallback)
-		pw.monitor.SetLogger(pw.Logger)
-		pw.Trace("enable health check monitor")
+		xl.Trace("enable health check monitor")
 	}
 	}
 
 
-	pw.pxy = NewProxy(pw.Cfg, clientCfg, serverUDPPort)
+	pw.pxy = NewProxy(pw.ctx, pw.Cfg, clientCfg, serverUDPPort)
 	return pw
 	return pw
 }
 }
 
 
@@ -147,6 +149,7 @@ func (pw *ProxyWrapper) Stop() {
 }
 }
 
 
 func (pw *ProxyWrapper) checkWorker() {
 func (pw *ProxyWrapper) checkWorker() {
+	xl := pw.xl
 	if pw.monitor != nil {
 	if pw.monitor != nil {
 		// let monitor do check request first
 		// let monitor do check request first
 		time.Sleep(500 * time.Millisecond)
 		time.Sleep(500 * time.Millisecond)
@@ -161,7 +164,7 @@ func (pw *ProxyWrapper) checkWorker() {
 				(pw.Status == ProxyStatusWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) ||
 				(pw.Status == ProxyStatusWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) ||
 				(pw.Status == ProxyStatusStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) {
 				(pw.Status == ProxyStatusStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) {
 
 
-				pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusWaitStart)
+				xl.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusWaitStart)
 				pw.Status = ProxyStatusWaitStart
 				pw.Status = ProxyStatusWaitStart
 
 
 				var newProxyMsg msg.NewProxy
 				var newProxyMsg msg.NewProxy
@@ -180,7 +183,7 @@ func (pw *ProxyWrapper) checkWorker() {
 						ProxyName: pw.Name,
 						ProxyName: pw.Name,
 					},
 					},
 				})
 				})
-				pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed)
+				xl.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed)
 				pw.Status = ProxyStatusCheckFailed
 				pw.Status = ProxyStatusCheckFailed
 			}
 			}
 			pw.mu.Unlock()
 			pw.mu.Unlock()
@@ -196,6 +199,7 @@ func (pw *ProxyWrapper) checkWorker() {
 }
 }
 
 
 func (pw *ProxyWrapper) statusNormalCallback() {
 func (pw *ProxyWrapper) statusNormalCallback() {
+	xl := pw.xl
 	atomic.StoreUint32(&pw.health, 0)
 	atomic.StoreUint32(&pw.health, 0)
 	errors.PanicToError(func() {
 	errors.PanicToError(func() {
 		select {
 		select {
@@ -203,10 +207,11 @@ func (pw *ProxyWrapper) statusNormalCallback() {
 		default:
 		default:
 		}
 		}
 	})
 	})
-	pw.Info("health check success")
+	xl.Info("health check success")
 }
 }
 
 
 func (pw *ProxyWrapper) statusFailedCallback() {
 func (pw *ProxyWrapper) statusFailedCallback() {
+	xl := pw.xl
 	atomic.StoreUint32(&pw.health, 1)
 	atomic.StoreUint32(&pw.health, 1)
 	errors.PanicToError(func() {
 	errors.PanicToError(func() {
 		select {
 		select {
@@ -214,15 +219,16 @@ func (pw *ProxyWrapper) statusFailedCallback() {
 		default:
 		default:
 		}
 		}
 	})
 	})
-	pw.Info("health check failed")
+	xl.Info("health check failed")
 }
 }
 
 
-func (pw *ProxyWrapper) InWorkConn(workConn frpNet.Conn, m *msg.StartWorkConn) {
+func (pw *ProxyWrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) {
+	xl := pw.xl
 	pw.mu.RLock()
 	pw.mu.RLock()
 	pxy := pw.pxy
 	pxy := pw.pxy
 	pw.mu.RUnlock()
 	pw.mu.RUnlock()
 	if pxy != nil {
 	if pxy != nil {
-		workConn.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
+		xl.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
 		go pxy.InWorkConn(workConn, m)
 		go pxy.InWorkConn(workConn, m)
 	} else {
 	} else {
 		workConn.Close()
 		workConn.Close()

+ 32 - 17
client/service.go

@@ -15,9 +15,11 @@
 package client
 package client
 
 
 import (
 import (
+	"context"
 	"crypto/tls"
 	"crypto/tls"
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
+	"net"
 	"runtime"
 	"runtime"
 	"sync"
 	"sync"
 	"sync/atomic"
 	"sync/atomic"
@@ -30,6 +32,7 @@ import (
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
 	"github.com/fatedier/frp/utils/util"
 	"github.com/fatedier/frp/utils/util"
 	"github.com/fatedier/frp/utils/version"
 	"github.com/fatedier/frp/utils/version"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	fmux "github.com/hashicorp/yamux"
 	fmux "github.com/hashicorp/yamux"
 )
 )
@@ -55,19 +58,25 @@ type Service struct {
 	// This is configured by the login response from frps
 	// This is configured by the login response from frps
 	serverUDPPort int
 	serverUDPPort int
 
 
-	exit     uint32 // 0 means not exit
-	closedCh chan int
+	exit uint32 // 0 means not exit
+
+	// service context
+	ctx context.Context
+	// call cancel to stop service
+	cancel context.CancelFunc
 }
 }
 
 
-// NewService creates a new client service with the given configuration.
 func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (svr *Service, err error) {
 func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (svr *Service, err error) {
+
+	ctx, cancel := context.WithCancel(context.Background())
 	svr = &Service{
 	svr = &Service{
 		cfg:         cfg,
 		cfg:         cfg,
 		cfgFile:     cfgFile,
 		cfgFile:     cfgFile,
 		pxyCfgs:     pxyCfgs,
 		pxyCfgs:     pxyCfgs,
 		visitorCfgs: visitorCfgs,
 		visitorCfgs: visitorCfgs,
 		exit:        0,
 		exit:        0,
-		closedCh:    make(chan int),
+		ctx:         xlog.NewContext(ctx, xlog.New()),
+		cancel:      cancel,
 	}
 	}
 	return
 	return
 }
 }
@@ -79,11 +88,13 @@ func (svr *Service) GetController() *Control {
 }
 }
 
 
 func (svr *Service) Run() error {
 func (svr *Service) Run() error {
-	// first login
+	xl := xlog.FromContextSafe(svr.ctx)
+
+	// login to frps
 	for {
 	for {
 		conn, session, err := svr.login()
 		conn, session, err := svr.login()
 		if err != nil {
 		if err != nil {
-			log.Warn("login to server failed: %v", err)
+			xl.Warn("login to server failed: %v", err)
 
 
 			// if login_fail_exit is true, just exit this program
 			// if login_fail_exit is true, just exit this program
 			// otherwise sleep a while and try again to connect to server
 			// otherwise sleep a while and try again to connect to server
@@ -94,7 +105,7 @@ func (svr *Service) Run() error {
 			}
 			}
 		} else {
 		} else {
 			// login success
 			// login success
-			ctl := NewControl(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)
 			ctl.Run()
 			ctl.Run()
 			svr.ctlMu.Lock()
 			svr.ctlMu.Lock()
 			svr.ctl = ctl
 			svr.ctl = ctl
@@ -118,12 +129,12 @@ func (svr *Service) Run() error {
 		}
 		}
 		log.Info("admin server listen on %s:%d", svr.cfg.AdminAddr, svr.cfg.AdminPort)
 		log.Info("admin server listen on %s:%d", svr.cfg.AdminAddr, svr.cfg.AdminPort)
 	}
 	}
-
-	<-svr.closedCh
+	<-svr.ctx.Done()
 	return nil
 	return nil
 }
 }
 
 
 func (svr *Service) keepControllerWorking() {
 func (svr *Service) keepControllerWorking() {
+	xl := xlog.FromContextSafe(svr.ctx)
 	maxDelayTime := 20 * time.Second
 	maxDelayTime := 20 * time.Second
 	delayTime := time.Second
 	delayTime := time.Second
 
 
@@ -134,10 +145,10 @@ func (svr *Service) keepControllerWorking() {
 		}
 		}
 
 
 		for {
 		for {
-			log.Info("try to reconnect to server...")
+			xl.Info("try to reconnect to server...")
 			conn, session, err := svr.login()
 			conn, session, err := svr.login()
 			if err != nil {
 			if err != nil {
-				log.Warn("reconnect to server error: %v", err)
+				xl.Warn("reconnect to server error: %v", err)
 				time.Sleep(delayTime)
 				time.Sleep(delayTime)
 				delayTime = delayTime * 2
 				delayTime = delayTime * 2
 				if delayTime > maxDelayTime {
 				if delayTime > maxDelayTime {
@@ -148,7 +159,7 @@ func (svr *Service) keepControllerWorking() {
 			// reconnect success, init delayTime
 			// reconnect success, init delayTime
 			delayTime = time.Second
 			delayTime = time.Second
 
 
-			ctl := NewControl(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)
 			ctl.Run()
 			ctl.Run()
 			svr.ctlMu.Lock()
 			svr.ctlMu.Lock()
 			svr.ctl = ctl
 			svr.ctl = ctl
@@ -161,7 +172,8 @@ func (svr *Service) keepControllerWorking() {
 // login creates a connection to frps and registers it self as a client
 // login creates a connection to frps and registers it self as a client
 // conn: control connection
 // conn: control connection
 // session: if it's not nil, using tcp mux
 // session: if it's not nil, using tcp mux
-func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error) {
+func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
+	xl := xlog.FromContextSafe(svr.ctx)
 	var tlsConfig *tls.Config
 	var tlsConfig *tls.Config
 	if svr.cfg.TLSEnable {
 	if svr.cfg.TLSEnable {
 		tlsConfig = &tls.Config{
 		tlsConfig = &tls.Config{
@@ -197,7 +209,7 @@ func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error)
 			err = errRet
 			err = errRet
 			return
 			return
 		}
 		}
-		conn = frpNet.WrapConn(stream)
+		conn = stream
 	}
 	}
 
 
 	now := time.Now().Unix()
 	now := time.Now().Unix()
@@ -225,13 +237,16 @@ func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error)
 
 
 	if loginRespMsg.Error != "" {
 	if loginRespMsg.Error != "" {
 		err = fmt.Errorf("%s", loginRespMsg.Error)
 		err = fmt.Errorf("%s", loginRespMsg.Error)
-		log.Error("%s", loginRespMsg.Error)
+		xl.Error("%s", loginRespMsg.Error)
 		return
 		return
 	}
 	}
 
 
 	svr.runId = loginRespMsg.RunId
 	svr.runId = loginRespMsg.RunId
+	xl.ResetPrefixes()
+	xl.AppendPrefix(svr.runId)
+
 	svr.serverUDPPort = loginRespMsg.ServerUdpPort
 	svr.serverUDPPort = loginRespMsg.ServerUdpPort
-	log.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
+	xl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
 	return
 	return
 }
 }
 
 
@@ -247,5 +262,5 @@ func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs
 func (svr *Service) Close() {
 func (svr *Service) Close() {
 	atomic.StoreUint32(&svr.exit, 1)
 	atomic.StoreUint32(&svr.exit, 1)
 	svr.ctl.Close()
 	svr.ctl.Close()
-	close(svr.closedCh)
+	svr.cancel()
 }
 }

+ 56 - 50
client/visitor.go

@@ -16,6 +16,7 @@ package client
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"context"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -25,9 +26,9 @@ import (
 
 
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/msg"
-	"github.com/fatedier/frp/utils/log"
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
 	"github.com/fatedier/frp/utils/util"
 	"github.com/fatedier/frp/utils/util"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	frpIo "github.com/fatedier/golib/io"
 	frpIo "github.com/fatedier/golib/io"
 	"github.com/fatedier/golib/pool"
 	"github.com/fatedier/golib/pool"
@@ -38,13 +39,13 @@ import (
 type Visitor interface {
 type Visitor interface {
 	Run() error
 	Run() error
 	Close()
 	Close()
-	log.Logger
 }
 }
 
 
-func NewVisitor(ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
+func NewVisitor(ctx context.Context, ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
+	xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(cfg.GetBaseInfo().ProxyName)
 	baseVisitor := BaseVisitor{
 	baseVisitor := BaseVisitor{
-		ctl:    ctl,
-		Logger: log.NewPrefixLogger(cfg.GetBaseInfo().ProxyName),
+		ctl: ctl,
+		ctx: xlog.NewContext(ctx, xl),
 	}
 	}
 	switch cfg := cfg.(type) {
 	switch cfg := cfg.(type) {
 	case *config.StcpVisitorConf:
 	case *config.StcpVisitorConf:
@@ -63,10 +64,11 @@ func NewVisitor(ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
 
 
 type BaseVisitor struct {
 type BaseVisitor struct {
 	ctl    *Control
 	ctl    *Control
-	l      frpNet.Listener
+	l      net.Listener
 	closed bool
 	closed bool
-	mu     sync.RWMutex
-	log.Logger
+
+	mu  sync.RWMutex
+	ctx context.Context
 }
 }
 
 
 type StcpVisitor struct {
 type StcpVisitor struct {
@@ -76,7 +78,7 @@ type StcpVisitor struct {
 }
 }
 
 
 func (sv *StcpVisitor) Run() (err error) {
 func (sv *StcpVisitor) Run() (err error) {
-	sv.l, err = frpNet.ListenTcp(sv.cfg.BindAddr, sv.cfg.BindPort)
+	sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
 	if err != nil {
 	if err != nil {
 		return
 		return
 	}
 	}
@@ -90,10 +92,11 @@ func (sv *StcpVisitor) Close() {
 }
 }
 
 
 func (sv *StcpVisitor) worker() {
 func (sv *StcpVisitor) worker() {
+	xl := xlog.FromContextSafe(sv.ctx)
 	for {
 	for {
 		conn, err := sv.l.Accept()
 		conn, err := sv.l.Accept()
 		if err != nil {
 		if err != nil {
-			sv.Warn("stcp local listener closed")
+			xl.Warn("stcp local listener closed")
 			return
 			return
 		}
 		}
 
 
@@ -101,10 +104,11 @@ func (sv *StcpVisitor) worker() {
 	}
 	}
 }
 }
 
 
-func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
+func (sv *StcpVisitor) handleConn(userConn net.Conn) {
+	xl := xlog.FromContextSafe(sv.ctx)
 	defer userConn.Close()
 	defer userConn.Close()
 
 
-	sv.Debug("get a new stcp user connection")
+	xl.Debug("get a new stcp user connection")
 	visitorConn, err := sv.ctl.connectServer()
 	visitorConn, err := sv.ctl.connectServer()
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -121,7 +125,7 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
 	}
 	}
 	err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
 	err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
 	if err != nil {
 	if err != nil {
-		sv.Warn("send newVisitorConnMsg to server error: %v", err)
+		xl.Warn("send newVisitorConnMsg to server error: %v", err)
 		return
 		return
 	}
 	}
 
 
@@ -129,13 +133,13 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
 	visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
 	visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
 	err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
 	err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
 	if err != nil {
 	if err != nil {
-		sv.Warn("get newVisitorConnRespMsg error: %v", err)
+		xl.Warn("get newVisitorConnRespMsg error: %v", err)
 		return
 		return
 	}
 	}
 	visitorConn.SetReadDeadline(time.Time{})
 	visitorConn.SetReadDeadline(time.Time{})
 
 
 	if newVisitorConnRespMsg.Error != "" {
 	if newVisitorConnRespMsg.Error != "" {
-		sv.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
+		xl.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
 		return
 		return
 	}
 	}
 
 
@@ -144,7 +148,7 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
 	if sv.cfg.UseEncryption {
 	if sv.cfg.UseEncryption {
 		remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk))
 		remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk))
 		if err != nil {
 		if err != nil {
-			sv.Error("create encryption stream error: %v", err)
+			xl.Error("create encryption stream error: %v", err)
 			return
 			return
 		}
 		}
 	}
 	}
@@ -163,7 +167,7 @@ type XtcpVisitor struct {
 }
 }
 
 
 func (sv *XtcpVisitor) Run() (err error) {
 func (sv *XtcpVisitor) Run() (err error) {
-	sv.l, err = frpNet.ListenTcp(sv.cfg.BindAddr, sv.cfg.BindPort)
+	sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
 	if err != nil {
 	if err != nil {
 		return
 		return
 	}
 	}
@@ -177,10 +181,11 @@ func (sv *XtcpVisitor) Close() {
 }
 }
 
 
 func (sv *XtcpVisitor) worker() {
 func (sv *XtcpVisitor) worker() {
+	xl := xlog.FromContextSafe(sv.ctx)
 	for {
 	for {
 		conn, err := sv.l.Accept()
 		conn, err := sv.l.Accept()
 		if err != nil {
 		if err != nil {
-			sv.Warn("xtcp local listener closed")
+			xl.Warn("xtcp local listener closed")
 			return
 			return
 		}
 		}
 
 
@@ -188,25 +193,26 @@ func (sv *XtcpVisitor) worker() {
 	}
 	}
 }
 }
 
 
-func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
+func (sv *XtcpVisitor) handleConn(userConn net.Conn) {
+	xl := xlog.FromContextSafe(sv.ctx)
 	defer userConn.Close()
 	defer userConn.Close()
 
 
-	sv.Debug("get a new xtcp user connection")
+	xl.Debug("get a new xtcp user connection")
 	if sv.ctl.serverUDPPort == 0 {
 	if sv.ctl.serverUDPPort == 0 {
-		sv.Error("xtcp is not supported by server")
+		xl.Error("xtcp is not supported by server")
 		return
 		return
 	}
 	}
 
 
 	raddr, err := net.ResolveUDPAddr("udp",
 	raddr, err := net.ResolveUDPAddr("udp",
 		fmt.Sprintf("%s:%d", sv.ctl.clientCfg.ServerAddr, sv.ctl.serverUDPPort))
 		fmt.Sprintf("%s:%d", sv.ctl.clientCfg.ServerAddr, sv.ctl.serverUDPPort))
 	if err != nil {
 	if err != nil {
-		sv.Error("resolve server UDP addr error")
+		xl.Error("resolve server UDP addr error")
 		return
 		return
 	}
 	}
 
 
 	visitorConn, err := net.DialUDP("udp", nil, raddr)
 	visitorConn, err := net.DialUDP("udp", nil, raddr)
 	if err != nil {
 	if err != nil {
-		sv.Warn("dial server udp addr error: %v", err)
+		xl.Warn("dial server udp addr error: %v", err)
 		return
 		return
 	}
 	}
 	defer visitorConn.Close()
 	defer visitorConn.Close()
@@ -219,7 +225,7 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 	}
 	}
 	err = msg.WriteMsg(visitorConn, natHoleVisitorMsg)
 	err = msg.WriteMsg(visitorConn, natHoleVisitorMsg)
 	if err != nil {
 	if err != nil {
-		sv.Warn("send natHoleVisitorMsg to server error: %v", err)
+		xl.Warn("send natHoleVisitorMsg to server error: %v", err)
 		return
 		return
 	}
 	}
 
 
@@ -229,24 +235,24 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 	buf := pool.GetBuf(1024)
 	buf := pool.GetBuf(1024)
 	n, err := visitorConn.Read(buf)
 	n, err := visitorConn.Read(buf)
 	if err != nil {
 	if err != nil {
-		sv.Warn("get natHoleRespMsg error: %v", err)
+		xl.Warn("get natHoleRespMsg error: %v", err)
 		return
 		return
 	}
 	}
 
 
 	err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
 	err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
 	if err != nil {
 	if err != nil {
-		sv.Warn("get natHoleRespMsg error: %v", err)
+		xl.Warn("get natHoleRespMsg error: %v", err)
 		return
 		return
 	}
 	}
 	visitorConn.SetReadDeadline(time.Time{})
 	visitorConn.SetReadDeadline(time.Time{})
 	pool.PutBuf(buf)
 	pool.PutBuf(buf)
 
 
 	if natHoleRespMsg.Error != "" {
 	if natHoleRespMsg.Error != "" {
-		sv.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
+		xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
 		return
 		return
 	}
 	}
 
 
-	sv.Trace("get natHoleRespMsg, sid [%s], client address [%s], visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
+	xl.Trace("get natHoleRespMsg, sid [%s], client address [%s], visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
 
 
 	// Close visitorConn, so we can use it's local address.
 	// Close visitorConn, so we can use it's local address.
 	visitorConn.Close()
 	visitorConn.Close()
@@ -255,12 +261,12 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 	laddr, _ := net.ResolveUDPAddr("udp", visitorConn.LocalAddr().String())
 	laddr, _ := net.ResolveUDPAddr("udp", visitorConn.LocalAddr().String())
 	daddr, err := net.ResolveUDPAddr("udp", natHoleRespMsg.ClientAddr)
 	daddr, err := net.ResolveUDPAddr("udp", natHoleRespMsg.ClientAddr)
 	if err != nil {
 	if err != nil {
-		sv.Error("resolve client udp address error: %v", err)
+		xl.Error("resolve client udp address error: %v", err)
 		return
 		return
 	}
 	}
 	lConn, err := net.DialUDP("udp", laddr, daddr)
 	lConn, err := net.DialUDP("udp", laddr, daddr)
 	if err != nil {
 	if err != nil {
-		sv.Error("dial client udp address error: %v", err)
+		xl.Error("dial client udp address error: %v", err)
 		return
 		return
 	}
 	}
 	defer lConn.Close()
 	defer lConn.Close()
@@ -272,53 +278,53 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 	lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
 	lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
 	n, err = lConn.Read(sidBuf)
 	n, err = lConn.Read(sidBuf)
 	if err != nil {
 	if err != nil {
-		sv.Warn("get sid from client error: %v", err)
+		xl.Warn("get sid from client error: %v", err)
 		return
 		return
 	}
 	}
 	lConn.SetReadDeadline(time.Time{})
 	lConn.SetReadDeadline(time.Time{})
 	if string(sidBuf[:n]) != natHoleRespMsg.Sid {
 	if string(sidBuf[:n]) != natHoleRespMsg.Sid {
-		sv.Warn("incorrect sid from client")
+		xl.Warn("incorrect sid from client")
 		return
 		return
 	}
 	}
 	pool.PutBuf(sidBuf)
 	pool.PutBuf(sidBuf)
 
 
-	sv.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
+	xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
 
 
 	// wrap kcp connection
 	// wrap kcp connection
 	var remote io.ReadWriteCloser
 	var remote io.ReadWriteCloser
 	remote, err = frpNet.NewKcpConnFromUdp(lConn, true, natHoleRespMsg.ClientAddr)
 	remote, err = frpNet.NewKcpConnFromUdp(lConn, true, natHoleRespMsg.ClientAddr)
 	if err != nil {
 	if err != nil {
-		sv.Error("create kcp connection from udp connection error: %v", err)
+		xl.Error("create kcp connection from udp connection error: %v", err)
 		return
 		return
 	}
 	}
 
 
-	if sv.cfg.UseEncryption {
-		remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk))
-		if err != nil {
-			sv.Error("create encryption stream error: %v", err)
-			return
-		}
-	}
-
-	if sv.cfg.UseCompression {
-		remote = frpIo.WithCompression(remote)
-	}
-
 	fmuxCfg := fmux.DefaultConfig()
 	fmuxCfg := fmux.DefaultConfig()
 	fmuxCfg.KeepAliveInterval = 5 * time.Second
 	fmuxCfg.KeepAliveInterval = 5 * time.Second
 	fmuxCfg.LogOutput = ioutil.Discard
 	fmuxCfg.LogOutput = ioutil.Discard
 	sess, err := fmux.Client(remote, fmuxCfg)
 	sess, err := fmux.Client(remote, fmuxCfg)
 	if err != nil {
 	if err != nil {
-		sv.Error("create yamux session error: %v", err)
+		xl.Error("create yamux session error: %v", err)
 		return
 		return
 	}
 	}
 	defer sess.Close()
 	defer sess.Close()
 	muxConn, err := sess.Open()
 	muxConn, err := sess.Open()
 	if err != nil {
 	if err != nil {
-		sv.Error("open yamux stream error: %v", err)
+		xl.Error("open yamux stream error: %v", err)
 		return
 		return
 	}
 	}
 
 
-	frpIo.Join(userConn, muxConn)
-	sv.Debug("join connections closed")
+	var muxConnRWCloser io.ReadWriteCloser = muxConn
+	if sv.cfg.UseEncryption {
+		muxConnRWCloser, err = frpIo.WithEncryption(muxConnRWCloser, []byte(sv.cfg.Sk))
+		if err != nil {
+			xl.Error("create encryption stream error: %v", err)
+			return
+		}
+	}
+	if sv.cfg.UseCompression {
+		muxConnRWCloser = frpIo.WithCompression(muxConnRWCloser)
+	}
+
+	frpIo.Join(userConn, muxConnRWCloser)
+	xl.Debug("join connections closed")
 }
 }

+ 15 - 9
client/visitor_manager.go

@@ -15,11 +15,12 @@
 package client
 package client
 
 
 import (
 import (
+	"context"
 	"sync"
 	"sync"
 	"time"
 	"time"
 
 
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/config"
-	"github.com/fatedier/frp/utils/log"
+	"github.com/fatedier/frp/utils/xlog"
 )
 )
 
 
 type VisitorManager struct {
 type VisitorManager struct {
@@ -30,26 +31,29 @@ type VisitorManager struct {
 
 
 	checkInterval time.Duration
 	checkInterval time.Duration
 
 
-	mu sync.Mutex
+	mu  sync.Mutex
+	ctx context.Context
 }
 }
 
 
-func NewVisitorManager(ctl *Control) *VisitorManager {
+func NewVisitorManager(ctx context.Context, ctl *Control) *VisitorManager {
 	return &VisitorManager{
 	return &VisitorManager{
 		ctl:           ctl,
 		ctl:           ctl,
 		cfgs:          make(map[string]config.VisitorConf),
 		cfgs:          make(map[string]config.VisitorConf),
 		visitors:      make(map[string]Visitor),
 		visitors:      make(map[string]Visitor),
 		checkInterval: 10 * time.Second,
 		checkInterval: 10 * time.Second,
+		ctx:           ctx,
 	}
 	}
 }
 }
 
 
 func (vm *VisitorManager) Run() {
 func (vm *VisitorManager) Run() {
+	xl := xlog.FromContextSafe(vm.ctx)
 	for {
 	for {
 		time.Sleep(vm.checkInterval)
 		time.Sleep(vm.checkInterval)
 		vm.mu.Lock()
 		vm.mu.Lock()
 		for _, cfg := range vm.cfgs {
 		for _, cfg := range vm.cfgs {
 			name := cfg.GetBaseInfo().ProxyName
 			name := cfg.GetBaseInfo().ProxyName
 			if _, exist := vm.visitors[name]; !exist {
 			if _, exist := vm.visitors[name]; !exist {
-				log.Info("try to start visitor [%s]", name)
+				xl.Info("try to start visitor [%s]", name)
 				vm.startVisitor(cfg)
 				vm.startVisitor(cfg)
 			}
 			}
 		}
 		}
@@ -59,19 +63,21 @@ func (vm *VisitorManager) Run() {
 
 
 // Hold lock before calling this function.
 // Hold lock before calling this function.
 func (vm *VisitorManager) startVisitor(cfg config.VisitorConf) (err error) {
 func (vm *VisitorManager) startVisitor(cfg config.VisitorConf) (err error) {
+	xl := xlog.FromContextSafe(vm.ctx)
 	name := cfg.GetBaseInfo().ProxyName
 	name := cfg.GetBaseInfo().ProxyName
-	visitor := NewVisitor(vm.ctl, cfg)
+	visitor := NewVisitor(vm.ctx, vm.ctl, cfg)
 	err = visitor.Run()
 	err = visitor.Run()
 	if err != nil {
 	if err != nil {
-		visitor.Warn("start error: %v", err)
+		xl.Warn("start error: %v", err)
 	} else {
 	} else {
 		vm.visitors[name] = visitor
 		vm.visitors[name] = visitor
-		visitor.Info("start visitor success")
+		xl.Info("start visitor success")
 	}
 	}
 	return
 	return
 }
 }
 
 
 func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
 func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
+	xl := xlog.FromContextSafe(vm.ctx)
 	vm.mu.Lock()
 	vm.mu.Lock()
 	defer vm.mu.Unlock()
 	defer vm.mu.Unlock()
 
 
@@ -97,7 +103,7 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
 		}
 		}
 	}
 	}
 	if len(delNames) > 0 {
 	if len(delNames) > 0 {
-		log.Info("visitor removed: %v", delNames)
+		xl.Info("visitor removed: %v", delNames)
 	}
 	}
 
 
 	addNames := make([]string, 0)
 	addNames := make([]string, 0)
@@ -109,7 +115,7 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
 		}
 		}
 	}
 	}
 	if len(addNames) > 0 {
 	if len(addNames) > 0 {
-		log.Info("visitor added: %v", addNames)
+		xl.Info("visitor added: %v", addNames)
 	}
 	}
 	return
 	return
 }
 }

+ 1 - 1
cmd/frps/root.go

@@ -202,7 +202,7 @@ func runServer(cfg config.ServerCommonConf) (err error) {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	log.Info("Start frps success")
+	log.Info("start frps success")
 	svr.Run()
 	svr.Run()
 	return
 	return
 }
 }

+ 0 - 14
go.sum

@@ -1,14 +1,10 @@
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
 github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
 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 h1:teH578mf2ii42NHhIp3PhgvjU5bv+NFMq9fSQR8NaG8=
 github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049/go.mod h1:DqIrnl0rp3Zybg9zbJmozTy1n8fYJoX+QoAj9slIkKM=
 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 h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
 github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
 github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
-github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
 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/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -26,28 +22,18 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8=
 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/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY=
-github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg=
 github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
 github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
-github.com/rodaine/table v1.0.0 h1:UaCJG5Axc/cNXVGXqnCrffm1KxP0OfYLe1HuJLf5sFY=
 github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I=
 github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I=
-github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 h1:K+jtWCOuZgCra7eXZ/VWn2FbJmrA/D058mTXhh2rq+8=
 github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
 github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
-github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 h1:pexgSe+JCFuxG+uoMZLO+ce8KHtdHGhst4cs6rw3gmk=
 github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
 github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
-github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw=
 github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
 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 h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks=
 github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
 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 h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
 github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
 github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=

+ 1 - 1
models/plugin/http_proxy.go

@@ -64,7 +64,7 @@ func (hp *HttpProxy) Name() string {
 	return PluginHttpProxy
 	return PluginHttpProxy
 }
 }
 
 
-func (hp *HttpProxy) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
+func (hp *HttpProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 
 
 	sc, rd := gnet.NewSharedConn(wrapConn)
 	sc, rd := gnet.NewSharedConn(wrapConn)

+ 2 - 1
models/plugin/https2http.go

@@ -18,6 +18,7 @@ import (
 	"crypto/tls"
 	"crypto/tls"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"net"
 	"net/http"
 	"net/http"
 	"net/http/httputil"
 	"net/http/httputil"
 	"strings"
 	"strings"
@@ -115,7 +116,7 @@ func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
 	return config, nil
 	return config, nil
 }
 }
 
 
-func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
+func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 	p.l.PutConn(wrapConn)
 	p.l.PutConn(wrapConn)
 }
 }

+ 3 - 3
models/plugin/plugin.go

@@ -20,8 +20,6 @@ import (
 	"net"
 	"net"
 	"sync"
 	"sync"
 
 
-	frpNet "github.com/fatedier/frp/utils/net"
-
 	"github.com/fatedier/golib/errors"
 	"github.com/fatedier/golib/errors"
 )
 )
 
 
@@ -46,7 +44,9 @@ func Create(name string, params map[string]string) (p Plugin, err error) {
 
 
 type Plugin interface {
 type Plugin interface {
 	Name() string
 	Name() string
-	Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte)
+
+	// extraBufToLocal will send to local connection first, then join conn with local connection
+	Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte)
 	Close() error
 	Close() error
 }
 }
 
 

+ 2 - 1
models/plugin/socks5.go

@@ -18,6 +18,7 @@ import (
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
+	"net"
 
 
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
 
 
@@ -53,7 +54,7 @@ func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
 	return
 	return
 }
 }
 
 
-func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
+func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
 	defer conn.Close()
 	defer conn.Close()
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 	sp.Server.ServeConn(wrapConn)
 	sp.Server.ServeConn(wrapConn)

+ 2 - 1
models/plugin/static_file.go

@@ -16,6 +16,7 @@ package plugin
 
 
 import (
 import (
 	"io"
 	"io"
+	"net"
 	"net/http"
 	"net/http"
 
 
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
@@ -72,7 +73,7 @@ func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
 	return sp, nil
 	return sp, nil
 }
 }
 
 
-func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
+func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 	wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
 	sp.l.PutConn(wrapConn)
 	sp.l.PutConn(wrapConn)
 }
 }

+ 1 - 3
models/plugin/unix_domain_socket.go

@@ -19,8 +19,6 @@ import (
 	"io"
 	"io"
 	"net"
 	"net"
 
 
-	frpNet "github.com/fatedier/frp/utils/net"
-
 	frpIo "github.com/fatedier/golib/io"
 	frpIo "github.com/fatedier/golib/io"
 )
 )
 
 
@@ -53,7 +51,7 @@ func NewUnixDomainSocketPlugin(params map[string]string) (p Plugin, err error) {
 	return
 	return
 }
 }
 
 
-func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
+func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
 	localConn, err := net.DialUnix("unix", nil, uds.UnixAddr)
 	localConn, err := net.DialUnix("unix", nil, uds.UnixAddr)
 	if err != nil {
 	if err != nil {
 		return
 		return

+ 1 - 0
models/proto/udp/udp.go

@@ -117,6 +117,7 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<-
 			if !ok {
 			if !ok {
 				udpConn, err = net.DialUDP("udp", nil, dstAddr)
 				udpConn, err = net.DialUDP("udp", nil, dstAddr)
 				if err != nil {
 				if err != nil {
+					mu.Unlock()
 					continue
 					continue
 				}
 				}
 				udpConnMap[udpMsg.RemoteAddr.String()] = udpConn
 				udpConnMap[udpMsg.RemoteAddr.String()] = udpConn

+ 47 - 33
server/control.go

@@ -15,8 +15,10 @@
 package server
 package server
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"net"
 	"runtime/debug"
 	"runtime/debug"
 	"sync"
 	"sync"
 	"time"
 	"time"
@@ -28,8 +30,8 @@ import (
 	"github.com/fatedier/frp/server/controller"
 	"github.com/fatedier/frp/server/controller"
 	"github.com/fatedier/frp/server/proxy"
 	"github.com/fatedier/frp/server/proxy"
 	"github.com/fatedier/frp/server/stats"
 	"github.com/fatedier/frp/server/stats"
-	"github.com/fatedier/frp/utils/net"
 	"github.com/fatedier/frp/utils/version"
 	"github.com/fatedier/frp/utils/version"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	"github.com/fatedier/golib/control/shutdown"
 	"github.com/fatedier/golib/control/shutdown"
 	"github.com/fatedier/golib/crypto"
 	"github.com/fatedier/golib/crypto"
@@ -131,9 +133,12 @@ type Control struct {
 
 
 	// Server configuration information
 	// Server configuration information
 	serverCfg config.ServerCommonConf
 	serverCfg config.ServerCommonConf
+
+	xl  *xlog.Logger
+	ctx context.Context
 }
 }
 
 
-func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManager,
+func NewControl(ctx context.Context, rc *controller.ResourceController, pxyManager *proxy.ProxyManager,
 	statsCollector stats.Collector, ctlConn net.Conn, loginMsg *msg.Login,
 	statsCollector stats.Collector, ctlConn net.Conn, loginMsg *msg.Login,
 	serverCfg config.ServerCommonConf) *Control {
 	serverCfg config.ServerCommonConf) *Control {
 
 
@@ -161,6 +166,8 @@ func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManage
 		managerShutdown: shutdown.New(),
 		managerShutdown: shutdown.New(),
 		allShutdown:     shutdown.New(),
 		allShutdown:     shutdown.New(),
 		serverCfg:       serverCfg,
 		serverCfg:       serverCfg,
+		xl:              xlog.FromContextSafe(ctx),
+		ctx:             ctx,
 	}
 	}
 }
 }
 
 
@@ -185,18 +192,19 @@ func (ctl *Control) Start() {
 }
 }
 
 
 func (ctl *Control) RegisterWorkConn(conn net.Conn) {
 func (ctl *Control) RegisterWorkConn(conn net.Conn) {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.conn.Error("panic error: %v", err)
-			ctl.conn.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 
 
 	select {
 	select {
 	case ctl.workConnCh <- conn:
 	case ctl.workConnCh <- conn:
-		ctl.conn.Debug("new work connection registered")
+		xl.Debug("new work connection registered")
 	default:
 	default:
-		ctl.conn.Debug("work connection pool is full, discarding")
+		xl.Debug("work connection pool is full, discarding")
 		conn.Close()
 		conn.Close()
 	}
 	}
 }
 }
@@ -206,10 +214,11 @@ func (ctl *Control) RegisterWorkConn(conn net.Conn) {
 // and wait until it is available.
 // and wait until it is available.
 // return an error if wait timeout
 // return an error if wait timeout
 func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
 func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.conn.Error("panic error: %v", err)
-			ctl.conn.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 
 
@@ -221,14 +230,14 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
 			err = frpErr.ErrCtlClosed
 			err = frpErr.ErrCtlClosed
 			return
 			return
 		}
 		}
-		ctl.conn.Debug("get work connection from pool")
+		xl.Debug("get work connection from pool")
 	default:
 	default:
 		// no work connections available in the poll, send message to frpc to get more
 		// no work connections available in the poll, send message to frpc to get more
 		err = errors.PanicToError(func() {
 		err = errors.PanicToError(func() {
 			ctl.sendCh <- &msg.ReqWorkConn{}
 			ctl.sendCh <- &msg.ReqWorkConn{}
 		})
 		})
 		if err != nil {
 		if err != nil {
-			ctl.conn.Error("%v", err)
+			xl.Error("%v", err)
 			return
 			return
 		}
 		}
 
 
@@ -236,13 +245,13 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
 		case workConn, ok = <-ctl.workConnCh:
 		case workConn, ok = <-ctl.workConnCh:
 			if !ok {
 			if !ok {
 				err = frpErr.ErrCtlClosed
 				err = frpErr.ErrCtlClosed
-				ctl.conn.Warn("no work connections avaiable, %v", err)
+				xl.Warn("no work connections avaiable, %v", err)
 				return
 				return
 			}
 			}
 
 
 		case <-time.After(time.Duration(ctl.serverCfg.UserConnTimeout) * time.Second):
 		case <-time.After(time.Duration(ctl.serverCfg.UserConnTimeout) * time.Second):
 			err = fmt.Errorf("timeout trying to get work connection")
 			err = fmt.Errorf("timeout trying to get work connection")
-			ctl.conn.Warn("%v", err)
+			xl.Warn("%v", err)
 			return
 			return
 		}
 		}
 	}
 	}
@@ -255,16 +264,18 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
 }
 }
 
 
 func (ctl *Control) Replaced(newCtl *Control) {
 func (ctl *Control) Replaced(newCtl *Control) {
-	ctl.conn.Info("Replaced by client [%s]", newCtl.runId)
+	xl := ctl.xl
+	xl.Info("Replaced by client [%s]", newCtl.runId)
 	ctl.runId = ""
 	ctl.runId = ""
 	ctl.allShutdown.Start()
 	ctl.allShutdown.Start()
 }
 }
 
 
 func (ctl *Control) writer() {
 func (ctl *Control) writer() {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.conn.Error("panic error: %v", err)
-			ctl.conn.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 
 
@@ -273,17 +284,17 @@ func (ctl *Control) writer() {
 
 
 	encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.serverCfg.Token))
 	encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.serverCfg.Token))
 	if err != nil {
 	if err != nil {
-		ctl.conn.Error("crypto new writer error: %v", err)
+		xl.Error("crypto new writer error: %v", err)
 		ctl.allShutdown.Start()
 		ctl.allShutdown.Start()
 		return
 		return
 	}
 	}
 	for {
 	for {
 		if m, ok := <-ctl.sendCh; !ok {
 		if m, ok := <-ctl.sendCh; !ok {
-			ctl.conn.Info("control writer is closing")
+			xl.Info("control writer is closing")
 			return
 			return
 		} else {
 		} else {
 			if err := msg.WriteMsg(encWriter, m); err != nil {
 			if err := msg.WriteMsg(encWriter, m); err != nil {
-				ctl.conn.Warn("write message to control connection error: %v", err)
+				xl.Warn("write message to control connection error: %v", err)
 				return
 				return
 			}
 			}
 		}
 		}
@@ -291,10 +302,11 @@ func (ctl *Control) writer() {
 }
 }
 
 
 func (ctl *Control) reader() {
 func (ctl *Control) reader() {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.conn.Error("panic error: %v", err)
-			ctl.conn.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 
 
@@ -305,10 +317,10 @@ func (ctl *Control) reader() {
 	for {
 	for {
 		if m, err := msg.ReadMsg(encReader); err != nil {
 		if m, err := msg.ReadMsg(encReader); err != nil {
 			if err == io.EOF {
 			if err == io.EOF {
-				ctl.conn.Debug("control connection closed")
+				xl.Debug("control connection closed")
 				return
 				return
 			} else {
 			} else {
-				ctl.conn.Warn("read error: %v", err)
+				xl.Warn("read error: %v", err)
 				ctl.conn.Close()
 				ctl.conn.Close()
 				return
 				return
 			}
 			}
@@ -319,10 +331,11 @@ func (ctl *Control) reader() {
 }
 }
 
 
 func (ctl *Control) stoper() {
 func (ctl *Control) stoper() {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.conn.Error("panic error: %v", err)
-			ctl.conn.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 
 
@@ -355,7 +368,7 @@ func (ctl *Control) stoper() {
 	}
 	}
 
 
 	ctl.allShutdown.Done()
 	ctl.allShutdown.Done()
-	ctl.conn.Info("client exit success")
+	xl.Info("client exit success")
 
 
 	ctl.statsCollector.Mark(stats.TypeCloseClient, &stats.CloseClientPayload{})
 	ctl.statsCollector.Mark(stats.TypeCloseClient, &stats.CloseClientPayload{})
 }
 }
@@ -366,10 +379,11 @@ func (ctl *Control) WaitClosed() {
 }
 }
 
 
 func (ctl *Control) manager() {
 func (ctl *Control) manager() {
+	xl := ctl.xl
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			ctl.conn.Error("panic error: %v", err)
-			ctl.conn.Error(string(debug.Stack()))
+			xl.Error("panic error: %v", err)
+			xl.Error(string(debug.Stack()))
 		}
 		}
 	}()
 	}()
 
 
@@ -383,7 +397,7 @@ func (ctl *Control) manager() {
 		select {
 		select {
 		case <-heartbeat.C:
 		case <-heartbeat.C:
 			if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartBeatTimeout)*time.Second {
 			if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartBeatTimeout)*time.Second {
-				ctl.conn.Warn("heartbeat timeout")
+				xl.Warn("heartbeat timeout")
 				return
 				return
 			}
 			}
 		case rawMsg, ok := <-ctl.readCh:
 		case rawMsg, ok := <-ctl.readCh:
@@ -400,10 +414,10 @@ func (ctl *Control) manager() {
 				}
 				}
 				if err != nil {
 				if err != nil {
 					resp.Error = err.Error()
 					resp.Error = err.Error()
-					ctl.conn.Warn("new proxy [%s] error: %v", m.ProxyName, err)
+					xl.Warn("new proxy [%s] error: %v", m.ProxyName, err)
 				} else {
 				} else {
 					resp.RemoteAddr = remoteAddr
 					resp.RemoteAddr = remoteAddr
-					ctl.conn.Info("new proxy [%s] success", m.ProxyName)
+					xl.Info("new proxy [%s] success", m.ProxyName)
 					ctl.statsCollector.Mark(stats.TypeNewProxy, &stats.NewProxyPayload{
 					ctl.statsCollector.Mark(stats.TypeNewProxy, &stats.NewProxyPayload{
 						Name:      m.ProxyName,
 						Name:      m.ProxyName,
 						ProxyType: m.ProxyType,
 						ProxyType: m.ProxyType,
@@ -412,10 +426,10 @@ func (ctl *Control) manager() {
 				ctl.sendCh <- resp
 				ctl.sendCh <- resp
 			case *msg.CloseProxy:
 			case *msg.CloseProxy:
 				ctl.CloseProxy(m)
 				ctl.CloseProxy(m)
-				ctl.conn.Info("close proxy [%s] success", m.ProxyName)
+				xl.Info("close proxy [%s] success", m.ProxyName)
 			case *msg.Ping:
 			case *msg.Ping:
 				ctl.lastPing = time.Now()
 				ctl.lastPing = time.Now()
-				ctl.conn.Debug("receive heartbeat")
+				xl.Debug("receive heartbeat")
 				ctl.sendCh <- &msg.Pong{}
 				ctl.sendCh <- &msg.Pong{}
 			}
 			}
 		}
 		}
@@ -432,7 +446,7 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
 
 
 	// NewProxy will return a interface Proxy.
 	// NewProxy will return a interface Proxy.
 	// In fact it create different proxies by different proxy type, we just call run() here.
 	// In fact it create different proxies by different proxy type, we just call run() here.
-	pxy, err := proxy.NewProxy(ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.serverCfg)
+	pxy, err := proxy.NewProxy(ctl.ctx, ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.serverCfg)
 	if err != nil {
 	if err != nil {
 		return remoteAddr, err
 		return remoteAddr, err
 	}
 	}

+ 2 - 1
server/controller/visitor.go

@@ -17,6 +17,7 @@ package controller
 import (
 import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"net"
 	"sync"
 	"sync"
 
 
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
@@ -55,7 +56,7 @@ func (vm *VisitorManager) Listen(name string, sk string) (l *frpNet.CustomListen
 	return
 	return
 }
 }
 
 
-func (vm *VisitorManager) NewConn(name string, conn frpNet.Conn, timestamp int64, signKey string,
+func (vm *VisitorManager) NewConn(name string, conn net.Conn, timestamp int64, signKey string,
 	useEncryption bool, useCompression bool) (err error) {
 	useEncryption bool, useCompression bool) (err error) {
 
 
 	vm.mu.RLock()
 	vm.mu.RLock()

+ 2 - 3
server/group/http.go

@@ -2,11 +2,10 @@ package group
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"net"
 	"sync"
 	"sync"
 	"sync/atomic"
 	"sync/atomic"
 
 
-	frpNet "github.com/fatedier/frp/utils/net"
-
 	"github.com/fatedier/frp/utils/vhost"
 	"github.com/fatedier/frp/utils/vhost"
 )
 )
 
 
@@ -131,7 +130,7 @@ func (g *HTTPGroup) UnRegister(proxyName string) (isEmpty bool) {
 	return
 	return
 }
 }
 
 
-func (g *HTTPGroup) createConn(remoteAddr string) (frpNet.Conn, error) {
+func (g *HTTPGroup) createConn(remoteAddr string) (net.Conn, error) {
 	var f vhost.CreateConnFunc
 	var f vhost.CreateConnFunc
 	newIndex := atomic.AddUint64(&g.index, 1)
 	newIndex := atomic.AddUint64(&g.index, 1)
 
 

+ 7 - 5
server/proxy/http.go

@@ -36,6 +36,7 @@ type HttpProxy struct {
 }
 }
 
 
 func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
 func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
+	xl := pxy.xl
 	routeConfig := vhost.VhostRouteConfig{
 	routeConfig := vhost.VhostRouteConfig{
 		RewriteHost:  pxy.cfg.HostHeaderRewrite,
 		RewriteHost:  pxy.cfg.HostHeaderRewrite,
 		Headers:      pxy.cfg.Headers,
 		Headers:      pxy.cfg.Headers,
@@ -88,7 +89,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
 				})
 				})
 			}
 			}
 			addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpPort)))
 			addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpPort)))
-			pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
+			xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
 		}
 		}
 	}
 	}
 
 
@@ -120,7 +121,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
 			}
 			}
 			addrs = append(addrs, util.CanonicalAddr(tmpDomain, pxy.serverCfg.VhostHttpPort))
 			addrs = append(addrs, util.CanonicalAddr(tmpDomain, pxy.serverCfg.VhostHttpPort))
 
 
-			pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
+			xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
 		}
 		}
 	}
 	}
 	remoteAddr = strings.Join(addrs, ",")
 	remoteAddr = strings.Join(addrs, ",")
@@ -131,10 +132,11 @@ func (pxy *HttpProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 	return pxy.cfg
 }
 }
 
 
-func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn frpNet.Conn, err error) {
+func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) {
+	xl := pxy.xl
 	rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)
 	rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)
 	if errRet != nil {
 	if errRet != nil {
-		pxy.Warn("resolve TCP addr [%s] error: %v", remoteAddr, errRet)
+		xl.Warn("resolve TCP addr [%s] error: %v", remoteAddr, errRet)
 		// we do not return error here since remoteAddr is not necessary for proxies without proxy protocol enabled
 		// we do not return error here since remoteAddr is not necessary for proxies without proxy protocol enabled
 	}
 	}
 
 
@@ -148,7 +150,7 @@ func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn frpNet.Conn, err
 	if pxy.cfg.UseEncryption {
 	if pxy.cfg.UseEncryption {
 		rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.serverCfg.Token))
 		rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.serverCfg.Token))
 		if err != nil {
 		if err != nil {
-			pxy.Error("create encryption stream error: %v", err)
+			xl.Error("create encryption stream error: %v", err)
 			return
 			return
 		}
 		}
 	}
 	}

+ 5 - 6
server/proxy/https.go

@@ -28,6 +28,7 @@ type HttpsProxy struct {
 }
 }
 
 
 func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
 func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
+	xl := pxy.xl
 	routeConfig := &vhost.VhostRouteConfig{}
 	routeConfig := &vhost.VhostRouteConfig{}
 
 
 	defer func() {
 	defer func() {
@@ -42,26 +43,24 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
 		}
 		}
 
 
 		routeConfig.Domain = domain
 		routeConfig.Domain = domain
-		l, errRet := pxy.rc.VhostHttpsMuxer.Listen(routeConfig)
+		l, errRet := pxy.rc.VhostHttpsMuxer.Listen(pxy.ctx, routeConfig)
 		if errRet != nil {
 		if errRet != nil {
 			err = errRet
 			err = errRet
 			return
 			return
 		}
 		}
-		l.AddLogPrefix(pxy.name)
-		pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
+		xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
 		pxy.listeners = append(pxy.listeners, l)
 		pxy.listeners = append(pxy.listeners, l)
 		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHttpsPort))
 		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHttpsPort))
 	}
 	}
 
 
 	if pxy.cfg.SubDomain != "" {
 	if pxy.cfg.SubDomain != "" {
 		routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost
 		routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost
-		l, errRet := pxy.rc.VhostHttpsMuxer.Listen(routeConfig)
+		l, errRet := pxy.rc.VhostHttpsMuxer.Listen(pxy.ctx, routeConfig)
 		if errRet != nil {
 		if errRet != nil {
 			err = errRet
 			err = errRet
 			return
 			return
 		}
 		}
-		l.AddLogPrefix(pxy.name)
-		pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
+		xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
 		pxy.listeners = append(pxy.listeners, l)
 		pxy.listeners = append(pxy.listeners, l)
 		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpsPort)))
 		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpsPort)))
 	}
 	}

+ 39 - 26
server/proxy/proxy.go

@@ -15,6 +15,7 @@
 package proxy
 package proxy
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"net"
 	"net"
@@ -25,48 +26,54 @@ import (
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/server/controller"
 	"github.com/fatedier/frp/server/controller"
 	"github.com/fatedier/frp/server/stats"
 	"github.com/fatedier/frp/server/stats"
-	"github.com/fatedier/frp/utils/log"
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	frpIo "github.com/fatedier/golib/io"
 	frpIo "github.com/fatedier/golib/io"
 )
 )
 
 
-type GetWorkConnFn func() (frpNet.Conn, error)
+type GetWorkConnFn func() (net.Conn, error)
 
 
 type Proxy interface {
 type Proxy interface {
+	Context() context.Context
 	Run() (remoteAddr string, err error)
 	Run() (remoteAddr string, err error)
 	GetName() string
 	GetName() string
 	GetConf() config.ProxyConf
 	GetConf() config.ProxyConf
-	GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Conn, err error)
+	GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn, err error)
 	GetUsedPortsNum() int
 	GetUsedPortsNum() int
 	Close()
 	Close()
-	log.Logger
 }
 }
 
 
 type BaseProxy struct {
 type BaseProxy struct {
 	name           string
 	name           string
 	rc             *controller.ResourceController
 	rc             *controller.ResourceController
 	statsCollector stats.Collector
 	statsCollector stats.Collector
-	listeners      []frpNet.Listener
+	listeners      []net.Listener
 	usedPortsNum   int
 	usedPortsNum   int
 	poolCount      int
 	poolCount      int
 	getWorkConnFn  GetWorkConnFn
 	getWorkConnFn  GetWorkConnFn
 	serverCfg      config.ServerCommonConf
 	serverCfg      config.ServerCommonConf
 
 
-	mu sync.RWMutex
-	log.Logger
+	mu  sync.RWMutex
+	xl  *xlog.Logger
+	ctx context.Context
 }
 }
 
 
 func (pxy *BaseProxy) GetName() string {
 func (pxy *BaseProxy) GetName() string {
 	return pxy.name
 	return pxy.name
 }
 }
 
 
+func (pxy *BaseProxy) Context() context.Context {
+	return pxy.ctx
+}
+
 func (pxy *BaseProxy) GetUsedPortsNum() int {
 func (pxy *BaseProxy) GetUsedPortsNum() int {
 	return pxy.usedPortsNum
 	return pxy.usedPortsNum
 }
 }
 
 
 func (pxy *BaseProxy) Close() {
 func (pxy *BaseProxy) Close() {
-	pxy.Info("proxy closing")
+	xl := xlog.FromContextSafe(pxy.ctx)
+	xl.Info("proxy closing")
 	for _, l := range pxy.listeners {
 	for _, l := range pxy.listeners {
 		l.Close()
 		l.Close()
 	}
 	}
@@ -74,15 +81,17 @@ func (pxy *BaseProxy) Close() {
 
 
 // GetWorkConnFromPool try to get a new work connections from pool
 // GetWorkConnFromPool try to get a new work connections from pool
 // for quickly response, we immediately send the StartWorkConn message to frpc after take out one from pool
 // for quickly response, we immediately send the StartWorkConn message to frpc after take out one from pool
-func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Conn, err error) {
+func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn, err error) {
+	xl := xlog.FromContextSafe(pxy.ctx)
 	// try all connections from the pool
 	// try all connections from the pool
 	for i := 0; i < pxy.poolCount+1; i++ {
 	for i := 0; i < pxy.poolCount+1; i++ {
 		if workConn, err = pxy.getWorkConnFn(); err != nil {
 		if workConn, err = pxy.getWorkConnFn(); err != nil {
-			pxy.Warn("failed to get work connection: %v", err)
+			xl.Warn("failed to get work connection: %v", err)
 			return
 			return
 		}
 		}
-		pxy.Info("get a new work connection: [%s]", workConn.RemoteAddr().String())
-		workConn.AddLogPrefix(pxy.GetName())
+		xl.Info("get a new work connection: [%s]", workConn.RemoteAddr().String())
+		xl.Spawn().AppendPrefix(pxy.GetName())
+		workConn = frpNet.NewContextConn(workConn, pxy.ctx)
 
 
 		var (
 		var (
 			srcAddr    string
 			srcAddr    string
@@ -109,7 +118,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Co
 			DstPort:   uint16(dstPort),
 			DstPort:   uint16(dstPort),
 		})
 		})
 		if err != nil {
 		if err != nil {
-			workConn.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
+			xl.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
 			workConn.Close()
 			workConn.Close()
 		} else {
 		} else {
 			break
 			break
@@ -117,7 +126,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Co
 	}
 	}
 
 
 	if err != nil {
 	if err != nil {
-		pxy.Error("try to get work connection failed in the end")
+		xl.Error("try to get work connection failed in the end")
 		return
 		return
 	}
 	}
 	return
 	return
@@ -126,36 +135,39 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Co
 // startListenHandler start a goroutine handler for each listener.
 // startListenHandler start a goroutine handler for each listener.
 // p: p will just be passed to handler(Proxy, frpNet.Conn).
 // 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.
 // 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, frpNet.Conn, stats.Collector, config.ServerCommonConf)) {
+func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn, stats.Collector, config.ServerCommonConf)) {
+	xl := xlog.FromContextSafe(pxy.ctx)
 	for _, listener := range pxy.listeners {
 	for _, listener := range pxy.listeners {
-		go func(l frpNet.Listener) {
+		go func(l net.Listener) {
 			for {
 			for {
 				// block
 				// block
 				// if listener is closed, err returned
 				// if listener is closed, err returned
 				c, err := l.Accept()
 				c, err := l.Accept()
 				if err != nil {
 				if err != nil {
-					pxy.Info("listener is closed")
+					xl.Info("listener is closed")
 					return
 					return
 				}
 				}
-				pxy.Debug("get a user connection [%s]", c.RemoteAddr().String())
+				xl.Debug("get a user connection [%s]", c.RemoteAddr().String())
 				go handler(p, c, pxy.statsCollector, pxy.serverCfg)
 				go handler(p, c, pxy.statsCollector, pxy.serverCfg)
 			}
 			}
 		}(listener)
 		}(listener)
 	}
 	}
 }
 }
 
 
-func NewProxy(runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int,
+func NewProxy(ctx context.Context, runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int,
 	getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf) (pxy Proxy, err error) {
 	getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf) (pxy Proxy, err error) {
 
 
+	xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(pxyConf.GetBaseInfo().ProxyName)
 	basePxy := BaseProxy{
 	basePxy := BaseProxy{
 		name:           pxyConf.GetBaseInfo().ProxyName,
 		name:           pxyConf.GetBaseInfo().ProxyName,
 		rc:             rc,
 		rc:             rc,
 		statsCollector: statsCollector,
 		statsCollector: statsCollector,
-		listeners:      make([]frpNet.Listener, 0),
+		listeners:      make([]net.Listener, 0),
 		poolCount:      poolCount,
 		poolCount:      poolCount,
 		getWorkConnFn:  getWorkConnFn,
 		getWorkConnFn:  getWorkConnFn,
-		Logger:         log.NewPrefixLogger(runId),
 		serverCfg:      serverCfg,
 		serverCfg:      serverCfg,
+		xl:             xl,
+		ctx:            xlog.NewContext(ctx, xl),
 	}
 	}
 	switch cfg := pxyConf.(type) {
 	switch cfg := pxyConf.(type) {
 	case *config.TcpProxyConf:
 	case *config.TcpProxyConf:
@@ -193,13 +205,13 @@ func NewProxy(runId string, rc *controller.ResourceController, statsCollector st
 	default:
 	default:
 		return pxy, fmt.Errorf("proxy type not support")
 		return pxy, fmt.Errorf("proxy type not support")
 	}
 	}
-	pxy.AddLogPrefix(pxy.GetName())
 	return
 	return
 }
 }
 
 
 // HandleUserTcpConnection is used for incoming tcp user connections.
 // HandleUserTcpConnection is used for incoming tcp user connections.
 // It can be used for tcp, http, https type.
 // It can be used for tcp, http, https type.
-func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector stats.Collector, serverCfg config.ServerCommonConf) {
+func HandleUserTcpConnection(pxy Proxy, userConn net.Conn, statsCollector stats.Collector, serverCfg config.ServerCommonConf) {
+	xl := xlog.FromContextSafe(pxy.Context())
 	defer userConn.Close()
 	defer userConn.Close()
 
 
 	// try all connections from the pool
 	// try all connections from the pool
@@ -211,17 +223,18 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector sta
 
 
 	var local io.ReadWriteCloser = workConn
 	var local io.ReadWriteCloser = workConn
 	cfg := pxy.GetConf().GetBaseInfo()
 	cfg := pxy.GetConf().GetBaseInfo()
+	xl.Trace("handler user tcp connection, use_encryption: %t, use_compression: %t", cfg.UseEncryption, cfg.UseCompression)
 	if cfg.UseEncryption {
 	if cfg.UseEncryption {
 		local, err = frpIo.WithEncryption(local, []byte(serverCfg.Token))
 		local, err = frpIo.WithEncryption(local, []byte(serverCfg.Token))
 		if err != nil {
 		if err != nil {
-			pxy.Error("create encryption stream error: %v", err)
+			xl.Error("create encryption stream error: %v", err)
 			return
 			return
 		}
 		}
 	}
 	}
 	if cfg.UseCompression {
 	if cfg.UseCompression {
 		local = frpIo.WithCompression(local)
 		local = frpIo.WithCompression(local)
 	}
 	}
-	pxy.Debug("join connections, workConn(l[%s] r[%s]) userConn(l[%s] r[%s])", workConn.LocalAddr().String(),
+	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())
 		workConn.RemoteAddr().String(), userConn.LocalAddr().String(), userConn.RemoteAddr().String())
 
 
 	statsCollector.Mark(stats.TypeOpenConnection, &stats.OpenConnectionPayload{ProxyName: pxy.GetName()})
 	statsCollector.Mark(stats.TypeOpenConnection, &stats.OpenConnectionPayload{ProxyName: pxy.GetName()})
@@ -235,7 +248,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector sta
 		ProxyName:    pxy.GetName(),
 		ProxyName:    pxy.GetName(),
 		TrafficBytes: outCount,
 		TrafficBytes: outCount,
 	})
 	})
-	pxy.Debug("join connections closed")
+	xl.Debug("join connections closed")
 }
 }
 
 
 type ProxyManager struct {
 type ProxyManager struct {

+ 2 - 2
server/proxy/stcp.go

@@ -24,14 +24,14 @@ type StcpProxy struct {
 }
 }
 
 
 func (pxy *StcpProxy) Run() (remoteAddr string, err error) {
 func (pxy *StcpProxy) Run() (remoteAddr string, err error) {
+	xl := pxy.xl
 	listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk)
 	listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk)
 	if errRet != nil {
 	if errRet != nil {
 		err = errRet
 		err = errRet
 		return
 		return
 	}
 	}
-	listener.AddLogPrefix(pxy.name)
 	pxy.listeners = append(pxy.listeners, listener)
 	pxy.listeners = append(pxy.listeners, listener)
-	pxy.Info("stcp proxy custom listen success")
+	xl.Info("stcp proxy custom listen success")
 
 
 	pxy.startListenHandler(pxy, HandleUserTcpConnection)
 	pxy.startListenHandler(pxy, HandleUserTcpConnection)
 	return
 	return

+ 6 - 8
server/proxy/tcp.go

@@ -16,9 +16,9 @@ package proxy
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"net"
 
 
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/config"
-	frpNet "github.com/fatedier/frp/utils/net"
 )
 )
 
 
 type TcpProxy struct {
 type TcpProxy struct {
@@ -29,6 +29,7 @@ type TcpProxy struct {
 }
 }
 
 
 func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
 func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
+	xl := pxy.xl
 	if pxy.cfg.Group != "" {
 	if pxy.cfg.Group != "" {
 		l, realPort, errRet := pxy.rc.TcpGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
 		l, realPort, errRet := pxy.rc.TcpGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
 		if errRet != nil {
 		if errRet != nil {
@@ -41,10 +42,8 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
 			}
 			}
 		}()
 		}()
 		pxy.realPort = realPort
 		pxy.realPort = realPort
-		listener := frpNet.WrapLogListener(l)
-		listener.AddLogPrefix(pxy.name)
-		pxy.listeners = append(pxy.listeners, listener)
-		pxy.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group)
+		pxy.listeners = append(pxy.listeners, l)
+		xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group)
 	} else {
 	} else {
 		pxy.realPort, err = pxy.rc.TcpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
 		pxy.realPort, err = pxy.rc.TcpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
 		if err != nil {
 		if err != nil {
@@ -55,14 +54,13 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
 				pxy.rc.TcpPortManager.Release(pxy.realPort)
 				pxy.rc.TcpPortManager.Release(pxy.realPort)
 			}
 			}
 		}()
 		}()
-		listener, errRet := frpNet.ListenTcp(pxy.serverCfg.ProxyBindAddr, pxy.realPort)
+		listener, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
 		if errRet != nil {
 		if errRet != nil {
 			err = errRet
 			err = errRet
 			return
 			return
 		}
 		}
-		listener.AddLogPrefix(pxy.name)
 		pxy.listeners = append(pxy.listeners, listener)
 		pxy.listeners = append(pxy.listeners, listener)
-		pxy.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
+		xl.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
 	}
 	}
 
 
 	pxy.cfg.RemotePort = pxy.realPort
 	pxy.cfg.RemotePort = pxy.realPort

+ 12 - 11
server/proxy/udp.go

@@ -54,6 +54,7 @@ type UdpProxy struct {
 }
 }
 
 
 func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
+	xl := pxy.xl
 	pxy.realPort, err = pxy.rc.UdpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
 	pxy.realPort, err = pxy.rc.UdpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -74,10 +75,10 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 	udpConn, errRet := net.ListenUDP("udp", addr)
 	udpConn, errRet := net.ListenUDP("udp", addr)
 	if errRet != nil {
 	if errRet != nil {
 		err = errRet
 		err = errRet
-		pxy.Warn("listen udp port error: %v", err)
+		xl.Warn("listen udp port error: %v", err)
 		return
 		return
 	}
 	}
-	pxy.Info("udp proxy listen port [%d]", pxy.cfg.RemotePort)
+	xl.Info("udp proxy listen port [%d]", pxy.cfg.RemotePort)
 
 
 	pxy.udpConn = udpConn
 	pxy.udpConn = udpConn
 	pxy.sendCh = make(chan *msg.UdpPacket, 1024)
 	pxy.sendCh = make(chan *msg.UdpPacket, 1024)
@@ -91,11 +92,11 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 				rawMsg msg.Message
 				rawMsg msg.Message
 				errRet error
 				errRet error
 			)
 			)
-			pxy.Trace("loop waiting message from udp workConn")
+			xl.Trace("loop waiting message from udp workConn")
 			// client will send heartbeat in workConn for keeping alive
 			// client will send heartbeat in workConn for keeping alive
 			conn.SetReadDeadline(time.Now().Add(time.Duration(60) * time.Second))
 			conn.SetReadDeadline(time.Now().Add(time.Duration(60) * time.Second))
 			if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
 			if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
-				pxy.Warn("read from workConn for udp error: %v", errRet)
+				xl.Warn("read from workConn for udp error: %v", errRet)
 				conn.Close()
 				conn.Close()
 				// notify proxy to start a new work connection
 				// notify proxy to start a new work connection
 				// ignore error here, it means the proxy is closed
 				// ignore error here, it means the proxy is closed
@@ -107,11 +108,11 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 			conn.SetReadDeadline(time.Time{})
 			conn.SetReadDeadline(time.Time{})
 			switch m := rawMsg.(type) {
 			switch m := rawMsg.(type) {
 			case *msg.Ping:
 			case *msg.Ping:
-				pxy.Trace("udp work conn get ping message")
+				xl.Trace("udp work conn get ping message")
 				continue
 				continue
 			case *msg.UdpPacket:
 			case *msg.UdpPacket:
 				if errRet := errors.PanicToError(func() {
 				if errRet := errors.PanicToError(func() {
-					pxy.Trace("get udp message from workConn: %s", m.Content)
+					xl.Trace("get udp message from workConn: %s", m.Content)
 					pxy.readCh <- m
 					pxy.readCh <- m
 					pxy.statsCollector.Mark(stats.TypeAddTrafficOut, &stats.AddTrafficOutPayload{
 					pxy.statsCollector.Mark(stats.TypeAddTrafficOut, &stats.AddTrafficOutPayload{
 						ProxyName:    pxy.GetName(),
 						ProxyName:    pxy.GetName(),
@@ -119,7 +120,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 					})
 					})
 				}); errRet != nil {
 				}); errRet != nil {
 					conn.Close()
 					conn.Close()
-					pxy.Info("reader goroutine for udp work connection closed")
+					xl.Info("reader goroutine for udp work connection closed")
 					return
 					return
 				}
 				}
 			}
 			}
@@ -133,15 +134,15 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 			select {
 			select {
 			case udpMsg, ok := <-pxy.sendCh:
 			case udpMsg, ok := <-pxy.sendCh:
 				if !ok {
 				if !ok {
-					pxy.Info("sender goroutine for udp work connection closed")
+					xl.Info("sender goroutine for udp work connection closed")
 					return
 					return
 				}
 				}
 				if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
 				if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
-					pxy.Info("sender goroutine for udp work connection closed: %v", errRet)
+					xl.Info("sender goroutine for udp work connection closed: %v", errRet)
 					conn.Close()
 					conn.Close()
 					return
 					return
 				} else {
 				} else {
-					pxy.Trace("send message to udp workConn: %s", udpMsg.Content)
+					xl.Trace("send message to udp workConn: %s", udpMsg.Content)
 					pxy.statsCollector.Mark(stats.TypeAddTrafficIn, &stats.AddTrafficInPayload{
 					pxy.statsCollector.Mark(stats.TypeAddTrafficIn, &stats.AddTrafficInPayload{
 						ProxyName:    pxy.GetName(),
 						ProxyName:    pxy.GetName(),
 						TrafficBytes: int64(len(udpMsg.Content)),
 						TrafficBytes: int64(len(udpMsg.Content)),
@@ -149,7 +150,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 					continue
 					continue
 				}
 				}
 			case <-ctx.Done():
 			case <-ctx.Done():
-				pxy.Info("sender goroutine for udp work connection closed")
+				xl.Info("sender goroutine for udp work connection closed")
 				return
 				return
 			}
 			}
 		}
 		}

+ 6 - 4
server/proxy/xtcp.go

@@ -31,8 +31,10 @@ type XtcpProxy struct {
 }
 }
 
 
 func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
 func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
+	xl := pxy.xl
+
 	if pxy.rc.NatHoleController == nil {
 	if pxy.rc.NatHoleController == nil {
-		pxy.Error("udp port for xtcp is not specified.")
+		xl.Error("udp port for xtcp is not specified.")
 		err = fmt.Errorf("xtcp is not supported in frps")
 		err = fmt.Errorf("xtcp is not supported in frps")
 		return
 		return
 	}
 	}
@@ -53,7 +55,7 @@ func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
 				}
 				}
 				errRet = msg.WriteMsg(workConn, m)
 				errRet = msg.WriteMsg(workConn, m)
 				if errRet != nil {
 				if errRet != nil {
-					pxy.Warn("write nat hole sid package error, %v", errRet)
+					xl.Warn("write nat hole sid package error, %v", errRet)
 					workConn.Close()
 					workConn.Close()
 					break
 					break
 				}
 				}
@@ -61,12 +63,12 @@ func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
 				go func() {
 				go func() {
 					raw, errRet := msg.ReadMsg(workConn)
 					raw, errRet := msg.ReadMsg(workConn)
 					if errRet != nil {
 					if errRet != nil {
-						pxy.Warn("read nat hole client ok package error: %v", errRet)
+						xl.Warn("read nat hole client ok package error: %v", errRet)
 						workConn.Close()
 						workConn.Close()
 						return
 						return
 					}
 					}
 					if _, ok := raw.(*msg.NatHoleClientDetectOK); !ok {
 					if _, ok := raw.(*msg.NatHoleClientDetectOK); !ok {
-						pxy.Warn("read nat hole client ok package format error")
+						xl.Warn("read nat hole client ok package format error")
 						workConn.Close()
 						workConn.Close()
 						return
 						return
 					}
 					}

+ 38 - 31
server/service.go

@@ -16,6 +16,7 @@ package server
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"context"
 	"crypto/rand"
 	"crypto/rand"
 	"crypto/rsa"
 	"crypto/rsa"
 	"crypto/tls"
 	"crypto/tls"
@@ -42,6 +43,7 @@ import (
 	"github.com/fatedier/frp/utils/util"
 	"github.com/fatedier/frp/utils/util"
 	"github.com/fatedier/frp/utils/version"
 	"github.com/fatedier/frp/utils/version"
 	"github.com/fatedier/frp/utils/vhost"
 	"github.com/fatedier/frp/utils/vhost"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	"github.com/fatedier/golib/net/mux"
 	"github.com/fatedier/golib/net/mux"
 	fmux "github.com/hashicorp/yamux"
 	fmux "github.com/hashicorp/yamux"
@@ -57,16 +59,16 @@ type Service struct {
 	muxer *mux.Mux
 	muxer *mux.Mux
 
 
 	// Accept connections from client
 	// Accept connections from client
-	listener frpNet.Listener
+	listener net.Listener
 
 
 	// Accept connections using kcp
 	// Accept connections using kcp
-	kcpListener frpNet.Listener
+	kcpListener net.Listener
 
 
 	// Accept connections using websocket
 	// Accept connections using websocket
-	websocketListener frpNet.Listener
+	websocketListener net.Listener
 
 
 	// Accept frp tls connections
 	// Accept frp tls connections
-	tlsListener frpNet.Listener
+	tlsListener net.Listener
 
 
 	// Manage all controllers
 	// Manage all controllers
 	ctlManager *ControlManager
 	ctlManager *ControlManager
@@ -135,7 +137,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
 	go svr.muxer.Serve()
 	go svr.muxer.Serve()
 	ln = svr.muxer.DefaultListener()
 	ln = svr.muxer.DefaultListener()
 
 
-	svr.listener = frpNet.WrapLogListener(ln)
+	svr.listener = ln
 	log.Info("frps tcp listen on %s:%d", cfg.BindAddr, cfg.BindPort)
 	log.Info("frps tcp listen on %s:%d", cfg.BindAddr, cfg.BindPort)
 
 
 	// Listen for accepting connections from client using kcp protocol.
 	// Listen for accepting connections from client using kcp protocol.
@@ -194,7 +196,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
 			}
 			}
 		}
 		}
 
 
-		svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(frpNet.WrapLogListener(l), 30*time.Second)
+		svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, 30*time.Second)
 		if err != nil {
 		if err != nil {
 			err = fmt.Errorf("Create vhost httpsMuxer error, %v", err)
 			err = fmt.Errorf("Create vhost httpsMuxer error, %v", err)
 			return
 			return
@@ -203,10 +205,9 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
 	}
 	}
 
 
 	// frp tls listener
 	// frp tls listener
-	tlsListener := svr.muxer.Listen(1, 1, func(data []byte) bool {
+	svr.tlsListener = svr.muxer.Listen(1, 1, func(data []byte) bool {
 		return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE
 		return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE
 	})
 	})
-	svr.tlsListener = frpNet.WrapLogListener(tlsListener)
 
 
 	// Create nat hole controller.
 	// Create nat hole controller.
 	if cfg.BindUdpPort > 0 {
 	if cfg.BindUdpPort > 0 {
@@ -258,7 +259,7 @@ func (svr *Service) Run() {
 	svr.HandleListener(svr.listener)
 	svr.HandleListener(svr.listener)
 }
 }
 
 
-func (svr *Service) HandleListener(l frpNet.Listener) {
+func (svr *Service) HandleListener(l net.Listener) {
 	// Listen for incoming connections from client.
 	// Listen for incoming connections from client.
 	for {
 	for {
 		c, err := l.Accept()
 		c, err := l.Accept()
@@ -266,6 +267,9 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 			log.Warn("Listener for incoming connections from client closed")
 			log.Warn("Listener for incoming connections from client closed")
 			return
 			return
 		}
 		}
+		// inject xlog object into net.Conn context
+		xl := xlog.New()
+		c = frpNet.NewContextConn(c, xlog.NewContext(context.Background(), xl))
 
 
 		log.Trace("start check TLS connection...")
 		log.Trace("start check TLS connection...")
 		originConn := c
 		originConn := c
@@ -278,8 +282,8 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 		log.Trace("success check TLS connection")
 		log.Trace("success check TLS connection")
 
 
 		// Start a new goroutine for dealing connections.
 		// Start a new goroutine for dealing connections.
-		go func(frpConn frpNet.Conn) {
-			dealFn := func(conn frpNet.Conn) {
+		go func(frpConn net.Conn) {
+			dealFn := func(conn net.Conn) {
 				var rawMsg msg.Message
 				var rawMsg msg.Message
 				conn.SetReadDeadline(time.Now().Add(connReadTimeout))
 				conn.SetReadDeadline(time.Now().Add(connReadTimeout))
 				if rawMsg, err = msg.ReadMsg(conn); err != nil {
 				if rawMsg, err = msg.ReadMsg(conn); err != nil {
@@ -295,7 +299,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 					// If login failed, send error message there.
 					// If login failed, send error message there.
 					// Otherwise send success message in control's work goroutine.
 					// Otherwise send success message in control's work goroutine.
 					if err != nil {
 					if err != nil {
-						conn.Warn("%v", err)
+						xl.Warn("register control error: %v", err)
 						msg.WriteMsg(conn, &msg.LoginResp{
 						msg.WriteMsg(conn, &msg.LoginResp{
 							Version: version.Full(),
 							Version: version.Full(),
 							Error:   err.Error(),
 							Error:   err.Error(),
@@ -306,7 +310,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 					svr.RegisterWorkConn(conn, m)
 					svr.RegisterWorkConn(conn, m)
 				case *msg.NewVisitorConn:
 				case *msg.NewVisitorConn:
 					if err = svr.RegisterVisitorConn(conn, m); err != nil {
 					if err = svr.RegisterVisitorConn(conn, m); err != nil {
-						conn.Warn("%v", err)
+						xl.Warn("register visitor conn error: %v", err)
 						msg.WriteMsg(conn, &msg.NewVisitorConnResp{
 						msg.WriteMsg(conn, &msg.NewVisitorConnResp{
 							ProxyName: m.ProxyName,
 							ProxyName: m.ProxyName,
 							Error:     err.Error(),
 							Error:     err.Error(),
@@ -342,8 +346,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 						session.Close()
 						session.Close()
 						return
 						return
 					}
 					}
-					wrapConn := frpNet.WrapConn(stream)
-					go dealFn(wrapConn)
+					go dealFn(stream)
 				}
 				}
 			} else {
 			} else {
 				dealFn(frpConn)
 				dealFn(frpConn)
@@ -352,8 +355,21 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 	}
 	}
 }
 }
 
 
-func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (err error) {
-	ctlConn.Info("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]",
+func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err error) {
+	// If client's RunId is empty, it's a new client, we just create a new controller.
+	// Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one.
+	if loginMsg.RunId == "" {
+		loginMsg.RunId, err = util.RandId()
+		if err != nil {
+			return
+		}
+	}
+
+	ctx := frpNet.NewContextFromConn(ctlConn)
+	xl := xlog.FromContextSafe(ctx)
+	xl.AppendPrefix(loginMsg.RunId)
+	ctx = xlog.NewContext(ctx, xl)
+	xl.Info("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]",
 		ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
 		ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
 
 
 	// Check client version.
 	// Check client version.
@@ -368,22 +384,12 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
 		return
 		return
 	}
 	}
 
 
-	// If client's RunId is empty, it's a new client, we just create a new controller.
-	// Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one.
-	if loginMsg.RunId == "" {
-		loginMsg.RunId, err = util.RandId()
-		if err != nil {
-			return
-		}
-	}
-
-	ctl := NewControl(svr.rc, svr.pxyManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg)
+	ctl := NewControl(ctx, svr.rc, svr.pxyManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg)
 
 
 	if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil {
 	if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil {
 		oldCtl.allShutdown.WaitDone()
 		oldCtl.allShutdown.WaitDone()
 	}
 	}
 
 
-	ctlConn.AddLogPrefix(loginMsg.RunId)
 	ctl.Start()
 	ctl.Start()
 
 
 	// for statistics
 	// for statistics
@@ -398,17 +404,18 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
 }
 }
 
 
 // RegisterWorkConn register a new work connection to control and proxies need it.
 // RegisterWorkConn register a new work connection to control and proxies need it.
-func (svr *Service) RegisterWorkConn(workConn frpNet.Conn, newMsg *msg.NewWorkConn) {
+func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) {
+	xl := frpNet.NewLogFromConn(workConn)
 	ctl, exist := svr.ctlManager.GetById(newMsg.RunId)
 	ctl, exist := svr.ctlManager.GetById(newMsg.RunId)
 	if !exist {
 	if !exist {
-		workConn.Warn("No client control found for run id [%s]", newMsg.RunId)
+		xl.Warn("No client control found for run id [%s]", newMsg.RunId)
 		return
 		return
 	}
 	}
 	ctl.RegisterWorkConn(workConn)
 	ctl.RegisterWorkConn(workConn)
 	return
 	return
 }
 }
 
 
-func (svr *Service) RegisterVisitorConn(visitorConn frpNet.Conn, newMsg *msg.NewVisitorConn) error {
+func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVisitorConn) error {
 	return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
 	return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
 		newMsg.UseEncryption, newMsg.UseCompression)
 		newMsg.UseEncryption, newMsg.UseCompression)
 }
 }

+ 1 - 2
tests/ci/auto_test_frpc.ini

@@ -2,8 +2,7 @@
 server_addr = 127.0.0.1
 server_addr = 127.0.0.1
 server_port = 10700
 server_port = 10700
 log_file = console
 log_file = console
-# debug, info, warn, error
-log_level = debug
+log_level = trace
 token = 123456
 token = 123456
 admin_port = 10600
 admin_port = 10600
 admin_user = abc
 admin_user = abc

+ 1 - 2
tests/ci/auto_test_frps.ini

@@ -2,8 +2,7 @@
 bind_addr = 0.0.0.0
 bind_addr = 0.0.0.0
 bind_port = 10700
 bind_port = 10700
 vhost_http_port = 10804
 vhost_http_port = 10804
-log_file = console
-log_level = debug
+log_level = trace
 token = 123456
 token = 123456
 allow_ports = 10000-20000,20002,30000-50000
 allow_ports = 10000-20000,20002,30000-50000
 subdomain_host = sub.com
 subdomain_host = sub.com

+ 2 - 2
tests/mock/echo_server.go

@@ -11,7 +11,7 @@ import (
 )
 )
 
 
 type EchoServer struct {
 type EchoServer struct {
-	l frpNet.Listener
+	l net.Listener
 
 
 	port        int
 	port        int
 	repeatedNum int
 	repeatedNum int
@@ -30,7 +30,7 @@ func NewEchoServer(port int, repeatedNum int, specifyStr string) *EchoServer {
 }
 }
 
 
 func (es *EchoServer) Start() error {
 func (es *EchoServer) Start() error {
-	l, err := frpNet.ListenTcp("127.0.0.1", es.port)
+	l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", es.port))
 	if err != nil {
 	if err != nil {
 		fmt.Printf("echo server listen error: %v\n", err)
 		fmt.Printf("echo server listen error: %v\n", err)
 		return err
 		return err

+ 1 - 2
tests/util/util.go

@@ -14,7 +14,6 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/fatedier/frp/client"
 	"github.com/fatedier/frp/client"
-	frpNet "github.com/fatedier/frp/utils/net"
 )
 )
 
 
 func GetProxyStatus(statusAddr string, user string, passwd string, name string) (status *client.ProxyStatusResp, err error) {
 func GetProxyStatus(statusAddr string, user string, passwd string, name string) (status *client.ProxyStatusResp, err error) {
@@ -98,7 +97,7 @@ func ReloadConf(reloadAddr string, user string, passwd string) error {
 }
 }
 
 
 func SendTcpMsg(addr string, msg string) (res string, err error) {
 func SendTcpMsg(addr string, msg string) (res string, err error) {
-	c, err := frpNet.ConnectTcpServer(addr)
+	c, err := net.Dial("tcp", addr)
 	if err != nil {
 	if err != nil {
 		err = fmt.Errorf("connect to tcp server error: %v", err)
 		err = fmt.Errorf("connect to tcp server error: %v", err)
 		return
 		return

+ 0 - 68
utils/log/log.go

@@ -91,71 +91,3 @@ func Debug(format string, v ...interface{}) {
 func Trace(format string, v ...interface{}) {
 func Trace(format string, v ...interface{}) {
 	Log.Trace(format, v...)
 	Log.Trace(format, v...)
 }
 }
-
-// Logger is the log interface
-type Logger interface {
-	AddLogPrefix(string)
-	GetPrefixStr() string
-	GetAllPrefix() []string
-	ClearLogPrefix()
-	Error(string, ...interface{})
-	Warn(string, ...interface{})
-	Info(string, ...interface{})
-	Debug(string, ...interface{})
-	Trace(string, ...interface{})
-}
-
-type PrefixLogger struct {
-	prefix    string
-	allPrefix []string
-}
-
-func NewPrefixLogger(prefix string) *PrefixLogger {
-	logger := &PrefixLogger{
-		allPrefix: make([]string, 0),
-	}
-	logger.AddLogPrefix(prefix)
-	return logger
-}
-
-func (pl *PrefixLogger) AddLogPrefix(prefix string) {
-	if len(prefix) == 0 {
-		return
-	}
-
-	pl.prefix += "[" + prefix + "] "
-	pl.allPrefix = append(pl.allPrefix, prefix)
-}
-
-func (pl *PrefixLogger) GetPrefixStr() string {
-	return pl.prefix
-}
-
-func (pl *PrefixLogger) GetAllPrefix() []string {
-	return pl.allPrefix
-}
-
-func (pl *PrefixLogger) ClearLogPrefix() {
-	pl.prefix = ""
-	pl.allPrefix = make([]string, 0)
-}
-
-func (pl *PrefixLogger) Error(format string, v ...interface{}) {
-	Log.Error(pl.prefix+format, v...)
-}
-
-func (pl *PrefixLogger) Warn(format string, v ...interface{}) {
-	Log.Warn(pl.prefix+format, v...)
-}
-
-func (pl *PrefixLogger) Info(format string, v ...interface{}) {
-	Log.Info(pl.prefix+format, v...)
-}
-
-func (pl *PrefixLogger) Debug(format string, v ...interface{}) {
-	Log.Debug(pl.prefix+format, v...)
-}
-
-func (pl *PrefixLogger) Trace(format string, v ...interface{}) {
-	Log.Trace(pl.prefix+format, v...)
-}

+ 48 - 30
utils/net/conn.go

@@ -15,6 +15,7 @@
 package net
 package net
 
 
 import (
 import (
+	"context"
 	"crypto/tls"
 	"crypto/tls"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
@@ -23,41 +24,64 @@ import (
 	"sync/atomic"
 	"sync/atomic"
 	"time"
 	"time"
 
 
-	"github.com/fatedier/frp/utils/log"
-
+	"github.com/fatedier/frp/utils/xlog"
 	gnet "github.com/fatedier/golib/net"
 	gnet "github.com/fatedier/golib/net"
 	kcp "github.com/fatedier/kcp-go"
 	kcp "github.com/fatedier/kcp-go"
 )
 )
 
 
-// Conn is the interface of connections used in frp.
-type Conn interface {
-	net.Conn
-	log.Logger
+type ContextGetter interface {
+	Context() context.Context
+}
+
+type ContextSetter interface {
+	WithContext(ctx context.Context)
+}
+
+func NewLogFromConn(conn net.Conn) *xlog.Logger {
+	if c, ok := conn.(ContextGetter); ok {
+		return xlog.FromContextSafe(c.Context())
+	}
+	return xlog.New()
+}
+
+func NewContextFromConn(conn net.Conn) context.Context {
+	if c, ok := conn.(ContextGetter); ok {
+		return c.Context()
+	}
+	return context.Background()
 }
 }
 
 
-type WrapLogConn struct {
+// ContextConn is the connection with context
+type ContextConn struct {
 	net.Conn
 	net.Conn
-	log.Logger
+
+	ctx context.Context
 }
 }
 
 
-func WrapConn(c net.Conn) Conn {
-	return &WrapLogConn{
-		Conn:   c,
-		Logger: log.NewPrefixLogger(""),
+func NewContextConn(c net.Conn, ctx context.Context) *ContextConn {
+	return &ContextConn{
+		Conn: c,
+		ctx:  ctx,
 	}
 	}
 }
 }
 
 
+func (c *ContextConn) WithContext(ctx context.Context) {
+	c.ctx = ctx
+}
+
+func (c *ContextConn) Context() context.Context {
+	return c.ctx
+}
+
 type WrapReadWriteCloserConn struct {
 type WrapReadWriteCloserConn struct {
 	io.ReadWriteCloser
 	io.ReadWriteCloser
-	log.Logger
 
 
 	underConn net.Conn
 	underConn net.Conn
 }
 }
 
 
-func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) Conn {
+func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) net.Conn {
 	return &WrapReadWriteCloserConn{
 	return &WrapReadWriteCloserConn{
 		ReadWriteCloser: rwc,
 		ReadWriteCloser: rwc,
-		Logger:          log.NewPrefixLogger(""),
 		underConn:       underConn,
 		underConn:       underConn,
 	}
 	}
 }
 }
@@ -99,7 +123,6 @@ func (conn *WrapReadWriteCloserConn) SetWriteDeadline(t time.Time) error {
 
 
 type CloseNotifyConn struct {
 type CloseNotifyConn struct {
 	net.Conn
 	net.Conn
-	log.Logger
 
 
 	// 1 means closed
 	// 1 means closed
 	closeFlag int32
 	closeFlag int32
@@ -108,10 +131,9 @@ type CloseNotifyConn struct {
 }
 }
 
 
 // closeFn will be only called once
 // closeFn will be only called once
-func WrapCloseNotifyConn(c net.Conn, closeFn func()) Conn {
+func WrapCloseNotifyConn(c net.Conn, closeFn func()) net.Conn {
 	return &CloseNotifyConn{
 	return &CloseNotifyConn{
 		Conn:    c,
 		Conn:    c,
-		Logger:  log.NewPrefixLogger(""),
 		closeFn: closeFn,
 		closeFn: closeFn,
 	}
 	}
 }
 }
@@ -128,7 +150,7 @@ func (cc *CloseNotifyConn) Close() (err error) {
 }
 }
 
 
 type StatsConn struct {
 type StatsConn struct {
-	Conn
+	net.Conn
 
 
 	closed     int64 // 1 means closed
 	closed     int64 // 1 means closed
 	totalRead  int64
 	totalRead  int64
@@ -136,7 +158,7 @@ type StatsConn struct {
 	statsFunc  func(totalRead, totalWrite int64)
 	statsFunc  func(totalRead, totalWrite int64)
 }
 }
 
 
-func WrapStatsConn(conn Conn, statsFunc func(total, totalWrite int64)) *StatsConn {
+func WrapStatsConn(conn net.Conn, statsFunc func(total, totalWrite int64)) *StatsConn {
 	return &StatsConn{
 	return &StatsConn{
 		Conn:      conn,
 		Conn:      conn,
 		statsFunc: statsFunc,
 		statsFunc: statsFunc,
@@ -166,10 +188,10 @@ func (statsConn *StatsConn) Close() (err error) {
 	return
 	return
 }
 }
 
 
-func ConnectServer(protocol string, addr string) (c Conn, err error) {
+func ConnectServer(protocol string, addr string) (c net.Conn, err error) {
 	switch protocol {
 	switch protocol {
 	case "tcp":
 	case "tcp":
-		return ConnectTcpServer(addr)
+		return net.Dial("tcp", addr)
 	case "kcp":
 	case "kcp":
 		kcpConn, errRet := kcp.DialWithOptions(addr, nil, 10, 3)
 		kcpConn, errRet := kcp.DialWithOptions(addr, nil, 10, 3)
 		if errRet != nil {
 		if errRet != nil {
@@ -184,21 +206,17 @@ func ConnectServer(protocol string, addr string) (c Conn, err error) {
 		kcpConn.SetACKNoDelay(false)
 		kcpConn.SetACKNoDelay(false)
 		kcpConn.SetReadBuffer(4194304)
 		kcpConn.SetReadBuffer(4194304)
 		kcpConn.SetWriteBuffer(4194304)
 		kcpConn.SetWriteBuffer(4194304)
-		c = WrapConn(kcpConn)
+		c = kcpConn
 		return
 		return
 	default:
 	default:
 		return nil, fmt.Errorf("unsupport protocol: %s", protocol)
 		return nil, fmt.Errorf("unsupport protocol: %s", protocol)
 	}
 	}
 }
 }
 
 
-func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn, err error) {
+func ConnectServerByProxy(proxyURL string, protocol string, addr string) (c net.Conn, err error) {
 	switch protocol {
 	switch protocol {
 	case "tcp":
 	case "tcp":
-		var conn net.Conn
-		if conn, err = gnet.DialTcpByProxy(proxyUrl, addr); err != nil {
-			return
-		}
-		return WrapConn(conn), nil
+		return gnet.DialTcpByProxy(proxyURL, addr)
 	case "kcp":
 	case "kcp":
 		// http proxy is not supported for kcp
 		// http proxy is not supported for kcp
 		return ConnectServer(protocol, addr)
 		return ConnectServer(protocol, addr)
@@ -209,7 +227,7 @@ func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn
 	}
 	}
 }
 }
 
 
-func ConnectServerByProxyWithTLS(proxyUrl string, protocol string, addr string, tlsConfig *tls.Config) (c Conn, err error) {
+func ConnectServerByProxyWithTLS(proxyUrl string, protocol string, addr string, tlsConfig *tls.Config) (c net.Conn, err error) {
 	c, err = ConnectServerByProxy(proxyUrl, protocol, addr)
 	c, err = ConnectServerByProxy(proxyUrl, protocol, addr)
 	if err != nil {
 	if err != nil {
 		return
 		return

+ 10 - 12
utils/net/kcp.go

@@ -18,17 +18,13 @@ import (
 	"fmt"
 	"fmt"
 	"net"
 	"net"
 
 
-	"github.com/fatedier/frp/utils/log"
-
 	kcp "github.com/fatedier/kcp-go"
 	kcp "github.com/fatedier/kcp-go"
 )
 )
 
 
 type KcpListener struct {
 type KcpListener struct {
-	net.Addr
 	listener  net.Listener
 	listener  net.Listener
-	accept    chan Conn
+	acceptCh  chan net.Conn
 	closeFlag bool
 	closeFlag bool
-	log.Logger
 }
 }
 
 
 func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
 func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
@@ -40,11 +36,9 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
 	listener.SetWriteBuffer(4194304)
 	listener.SetWriteBuffer(4194304)
 
 
 	l = &KcpListener{
 	l = &KcpListener{
-		Addr:      listener.Addr(),
 		listener:  listener,
 		listener:  listener,
-		accept:    make(chan Conn),
+		acceptCh:  make(chan net.Conn),
 		closeFlag: false,
 		closeFlag: false,
-		Logger:    log.NewPrefixLogger(""),
 	}
 	}
 
 
 	go func() {
 	go func() {
@@ -52,7 +46,7 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
 			conn, err := listener.AcceptKCP()
 			conn, err := listener.AcceptKCP()
 			if err != nil {
 			if err != nil {
 				if l.closeFlag {
 				if l.closeFlag {
-					close(l.accept)
+					close(l.acceptCh)
 					return
 					return
 				}
 				}
 				continue
 				continue
@@ -64,14 +58,14 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
 			conn.SetWindowSize(1024, 1024)
 			conn.SetWindowSize(1024, 1024)
 			conn.SetACKNoDelay(false)
 			conn.SetACKNoDelay(false)
 
 
-			l.accept <- WrapConn(conn)
+			l.acceptCh <- conn
 		}
 		}
 	}()
 	}()
 	return l, err
 	return l, err
 }
 }
 
 
-func (l *KcpListener) Accept() (Conn, error) {
-	conn, ok := <-l.accept
+func (l *KcpListener) Accept() (net.Conn, error) {
+	conn, ok := <-l.acceptCh
 	if !ok {
 	if !ok {
 		return conn, fmt.Errorf("channel for kcp listener closed")
 		return conn, fmt.Errorf("channel for kcp listener closed")
 	}
 	}
@@ -86,6 +80,10 @@ func (l *KcpListener) Close() error {
 	return nil
 	return nil
 }
 }
 
 
+func (l *KcpListener) Addr() net.Addr {
+	return l.listener.Addr()
+}
+
 func NewKcpConnFromUdp(conn *net.UDPConn, connected bool, raddr string) (net.Conn, error) {
 func NewKcpConnFromUdp(conn *net.UDPConn, connected bool, raddr string) (net.Conn, error) {
 	kcpConn, err := kcp.NewConnEx(1, connected, raddr, nil, 10, 3, conn)
 	kcpConn, err := kcp.NewConnEx(1, connected, raddr, nil, 10, 3, conn)
 	if err != nil {
 	if err != nil {

+ 9 - 40
utils/net/listener.go

@@ -19,65 +19,34 @@ import (
 	"net"
 	"net"
 	"sync"
 	"sync"
 
 
-	"github.com/fatedier/frp/utils/log"
-
 	"github.com/fatedier/golib/errors"
 	"github.com/fatedier/golib/errors"
 )
 )
 
 
-type Listener interface {
-	Accept() (Conn, error)
-	Close() error
-	log.Logger
-}
-
-type LogListener struct {
-	l net.Listener
-	net.Listener
-	log.Logger
-}
-
-func WrapLogListener(l net.Listener) Listener {
-	return &LogListener{
-		l:        l,
-		Listener: l,
-		Logger:   log.NewPrefixLogger(""),
-	}
-}
-
-func (logL *LogListener) Accept() (Conn, error) {
-	c, err := logL.l.Accept()
-	return WrapConn(c), err
-}
-
 // Custom listener
 // Custom listener
 type CustomListener struct {
 type CustomListener struct {
-	conns  chan Conn
-	closed bool
-	mu     sync.Mutex
-
-	log.Logger
+	acceptCh chan net.Conn
+	closed   bool
+	mu       sync.Mutex
 }
 }
 
 
 func NewCustomListener() *CustomListener {
 func NewCustomListener() *CustomListener {
 	return &CustomListener{
 	return &CustomListener{
-		conns:  make(chan Conn, 64),
-		Logger: log.NewPrefixLogger(""),
+		acceptCh: make(chan net.Conn, 64),
 	}
 	}
 }
 }
 
 
-func (l *CustomListener) Accept() (Conn, error) {
-	conn, ok := <-l.conns
+func (l *CustomListener) Accept() (net.Conn, error) {
+	conn, ok := <-l.acceptCh
 	if !ok {
 	if !ok {
 		return nil, fmt.Errorf("listener closed")
 		return nil, fmt.Errorf("listener closed")
 	}
 	}
-	conn.AddLogPrefix(l.GetPrefixStr())
 	return conn, nil
 	return conn, nil
 }
 }
 
 
-func (l *CustomListener) PutConn(conn Conn) error {
+func (l *CustomListener) PutConn(conn net.Conn) error {
 	err := errors.PanicToError(func() {
 	err := errors.PanicToError(func() {
 		select {
 		select {
-		case l.conns <- conn:
+		case l.acceptCh <- conn:
 		default:
 		default:
 			conn.Close()
 			conn.Close()
 		}
 		}
@@ -89,7 +58,7 @@ func (l *CustomListener) Close() error {
 	l.mu.Lock()
 	l.mu.Lock()
 	defer l.mu.Unlock()
 	defer l.mu.Unlock()
 	if !l.closed {
 	if !l.closed {
-		close(l.conns)
+		close(l.acceptCh)
 		l.closed = true
 		l.closed = true
 	}
 	}
 	return nil
 	return nil

+ 0 - 111
utils/net/tcp.go

@@ -1,111 +0,0 @@
-// Copyright 2016 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 (
-	"fmt"
-	"net"
-
-	"github.com/fatedier/frp/utils/log"
-)
-
-type TcpListener struct {
-	net.Addr
-	listener  net.Listener
-	accept    chan Conn
-	closeFlag bool
-	log.Logger
-}
-
-func ListenTcp(bindAddr string, bindPort int) (l *TcpListener, err error) {
-	tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
-	if err != nil {
-		return l, err
-	}
-	listener, err := net.ListenTCP("tcp", tcpAddr)
-	if err != nil {
-		return l, err
-	}
-
-	l = &TcpListener{
-		Addr:      listener.Addr(),
-		listener:  listener,
-		accept:    make(chan Conn),
-		closeFlag: false,
-		Logger:    log.NewPrefixLogger(""),
-	}
-
-	go func() {
-		for {
-			conn, err := listener.AcceptTCP()
-			if err != nil {
-				if l.closeFlag {
-					close(l.accept)
-					return
-				}
-				continue
-			}
-
-			c := NewTcpConn(conn)
-			l.accept <- c
-		}
-	}()
-	return l, err
-}
-
-// Wait util get one new connection or listener is closed
-// if listener is closed, err returned.
-func (l *TcpListener) Accept() (Conn, error) {
-	conn, ok := <-l.accept
-	if !ok {
-		return conn, fmt.Errorf("channel for tcp listener closed")
-	}
-	return conn, nil
-}
-
-func (l *TcpListener) Close() error {
-	if !l.closeFlag {
-		l.closeFlag = true
-		l.listener.Close()
-	}
-	return nil
-}
-
-// Wrap for TCPConn.
-type TcpConn struct {
-	net.Conn
-	log.Logger
-}
-
-func NewTcpConn(conn net.Conn) (c *TcpConn) {
-	c = &TcpConn{
-		Conn:   conn,
-		Logger: log.NewPrefixLogger(""),
-	}
-	return
-}
-
-func ConnectTcpServer(addr string) (c Conn, err error) {
-	servertAddr, err := net.ResolveTCPAddr("tcp", addr)
-	if err != nil {
-		return
-	}
-	conn, err := net.DialTCP("tcp", nil, servertAddr)
-	if err != nil {
-		return
-	}
-	c = NewTcpConn(conn)
-	return
-}

+ 5 - 5
utils/net/tls.go

@@ -26,13 +26,13 @@ var (
 	FRP_TLS_HEAD_BYTE = 0x17
 	FRP_TLS_HEAD_BYTE = 0x17
 )
 )
 
 
-func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out Conn) {
+func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out net.Conn) {
 	c.Write([]byte{byte(FRP_TLS_HEAD_BYTE)})
 	c.Write([]byte{byte(FRP_TLS_HEAD_BYTE)})
-	out = WrapConn(tls.Client(c, tlsConfig))
+	out = tls.Client(c, tlsConfig)
 	return
 	return
 }
 }
 
 
-func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, timeout time.Duration) (out Conn, err error) {
+func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, timeout time.Duration) (out net.Conn, err error) {
 	sc, r := gnet.NewSharedConnSize(c, 2)
 	sc, r := gnet.NewSharedConnSize(c, 2)
 	buf := make([]byte, 1)
 	buf := make([]byte, 1)
 	var n int
 	var n int
@@ -44,9 +44,9 @@ func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, t
 	}
 	}
 
 
 	if n == 1 && int(buf[0]) == FRP_TLS_HEAD_BYTE {
 	if n == 1 && int(buf[0]) == FRP_TLS_HEAD_BYTE {
-		out = WrapConn(tls.Server(c, tlsConfig))
+		out = tls.Server(c, tlsConfig)
 	} else {
 	} else {
-		out = WrapConn(sc)
+		out = sc
 	}
 	}
 	return
 	return
 }
 }

+ 13 - 17
utils/net/udp.go

@@ -21,8 +21,6 @@ import (
 	"sync"
 	"sync"
 	"time"
 	"time"
 
 
-	"github.com/fatedier/frp/utils/log"
-
 	"github.com/fatedier/golib/pool"
 	"github.com/fatedier/golib/pool"
 )
 )
 
 
@@ -33,7 +31,6 @@ type UdpPacket struct {
 }
 }
 
 
 type FakeUdpConn struct {
 type FakeUdpConn struct {
-	log.Logger
 	l *UdpListener
 	l *UdpListener
 
 
 	localAddr  net.Addr
 	localAddr  net.Addr
@@ -47,7 +44,6 @@ type FakeUdpConn struct {
 
 
 func NewFakeUdpConn(l *UdpListener, laddr, raddr net.Addr) *FakeUdpConn {
 func NewFakeUdpConn(l *UdpListener, laddr, raddr net.Addr) *FakeUdpConn {
 	fc := &FakeUdpConn{
 	fc := &FakeUdpConn{
-		Logger:     log.NewPrefixLogger(""),
 		l:          l,
 		l:          l,
 		localAddr:  laddr,
 		localAddr:  laddr,
 		remoteAddr: raddr,
 		remoteAddr: raddr,
@@ -157,15 +153,13 @@ func (c *FakeUdpConn) SetWriteDeadline(t time.Time) error {
 }
 }
 
 
 type UdpListener struct {
 type UdpListener struct {
-	net.Addr
-	accept    chan Conn
+	addr      net.Addr
+	acceptCh  chan net.Conn
 	writeCh   chan *UdpPacket
 	writeCh   chan *UdpPacket
 	readConn  net.Conn
 	readConn  net.Conn
 	closeFlag bool
 	closeFlag bool
 
 
 	fakeConns map[string]*FakeUdpConn
 	fakeConns map[string]*FakeUdpConn
-
-	log.Logger
 }
 }
 
 
 func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {
 func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {
@@ -176,11 +170,10 @@ func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {
 	readConn, err := net.ListenUDP("udp", udpAddr)
 	readConn, err := net.ListenUDP("udp", udpAddr)
 
 
 	l = &UdpListener{
 	l = &UdpListener{
-		Addr:      udpAddr,
-		accept:    make(chan Conn),
+		addr:      udpAddr,
+		acceptCh:  make(chan net.Conn),
 		writeCh:   make(chan *UdpPacket, 1000),
 		writeCh:   make(chan *UdpPacket, 1000),
 		fakeConns: make(map[string]*FakeUdpConn),
 		fakeConns: make(map[string]*FakeUdpConn),
-		Logger:    log.NewPrefixLogger(""),
 	}
 	}
 
 
 	// for reading
 	// for reading
@@ -189,19 +182,19 @@ func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {
 			buf := pool.GetBuf(1450)
 			buf := pool.GetBuf(1450)
 			n, remoteAddr, err := readConn.ReadFromUDP(buf)
 			n, remoteAddr, err := readConn.ReadFromUDP(buf)
 			if err != nil {
 			if err != nil {
-				close(l.accept)
+				close(l.acceptCh)
 				close(l.writeCh)
 				close(l.writeCh)
 				return
 				return
 			}
 			}
 
 
 			fakeConn, exist := l.fakeConns[remoteAddr.String()]
 			fakeConn, exist := l.fakeConns[remoteAddr.String()]
 			if !exist || fakeConn.IsClosed() {
 			if !exist || fakeConn.IsClosed() {
-				fakeConn = NewFakeUdpConn(l, l.Addr, remoteAddr)
+				fakeConn = NewFakeUdpConn(l, l.Addr(), remoteAddr)
 				l.fakeConns[remoteAddr.String()] = fakeConn
 				l.fakeConns[remoteAddr.String()] = fakeConn
 			}
 			}
 			fakeConn.putPacket(buf[:n])
 			fakeConn.putPacket(buf[:n])
 
 
-			l.accept <- fakeConn
+			l.acceptCh <- fakeConn
 		}
 		}
 	}()
 	}()
 
 
@@ -226,7 +219,6 @@ func (l *UdpListener) writeUdpPacket(packet *UdpPacket) (err error) {
 	defer func() {
 	defer func() {
 		if errRet := recover(); errRet != nil {
 		if errRet := recover(); errRet != nil {
 			err = fmt.Errorf("udp write closed listener")
 			err = fmt.Errorf("udp write closed listener")
-			l.Info("udp write closed listener")
 		}
 		}
 	}()
 	}()
 	l.writeCh <- packet
 	l.writeCh <- packet
@@ -243,8 +235,8 @@ func (l *UdpListener) WriteMsg(buf []byte, remoteAddr *net.UDPAddr) (err error)
 	return
 	return
 }
 }
 
 
-func (l *UdpListener) Accept() (Conn, error) {
-	conn, ok := <-l.accept
+func (l *UdpListener) Accept() (net.Conn, error) {
+	conn, ok := <-l.acceptCh
 	if !ok {
 	if !ok {
 		return conn, fmt.Errorf("channel for udp listener closed")
 		return conn, fmt.Errorf("channel for udp listener closed")
 	}
 	}
@@ -258,3 +250,7 @@ func (l *UdpListener) Close() error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+func (l *UdpListener) Addr() net.Addr {
+	return l.addr
+}

+ 12 - 15
utils/net/websocket.go

@@ -8,8 +8,6 @@ import (
 	"net/url"
 	"net/url"
 	"time"
 	"time"
 
 
-	"github.com/fatedier/frp/utils/log"
-
 	"golang.org/x/net/websocket"
 	"golang.org/x/net/websocket"
 )
 )
 
 
@@ -22,10 +20,8 @@ const (
 )
 )
 
 
 type WebsocketListener struct {
 type WebsocketListener struct {
-	net.Addr
-	ln     net.Listener
-	accept chan Conn
-	log.Logger
+	ln       net.Listener
+	acceptCh chan net.Conn
 
 
 	server    *http.Server
 	server    *http.Server
 	httpMutex *http.ServeMux
 	httpMutex *http.ServeMux
@@ -35,9 +31,7 @@ type WebsocketListener struct {
 // ln: tcp listener for websocket connections
 // ln: tcp listener for websocket connections
 func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
 func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
 	wl = &WebsocketListener{
 	wl = &WebsocketListener{
-		Addr:   ln.Addr(),
-		accept: make(chan Conn),
-		Logger: log.NewPrefixLogger(""),
+		acceptCh: make(chan net.Conn),
 	}
 	}
 
 
 	muxer := http.NewServeMux()
 	muxer := http.NewServeMux()
@@ -46,7 +40,7 @@ func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
 		conn := WrapCloseNotifyConn(c, func() {
 		conn := WrapCloseNotifyConn(c, func() {
 			close(notifyCh)
 			close(notifyCh)
 		})
 		})
-		wl.accept <- conn
+		wl.acceptCh <- conn
 		<-notifyCh
 		<-notifyCh
 	}))
 	}))
 
 
@@ -68,8 +62,8 @@ func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error)
 	return l, nil
 	return l, nil
 }
 }
 
 
-func (p *WebsocketListener) Accept() (Conn, error) {
-	c, ok := <-p.accept
+func (p *WebsocketListener) Accept() (net.Conn, error) {
+	c, ok := <-p.acceptCh
 	if !ok {
 	if !ok {
 		return nil, ErrWebsocketListenerClosed
 		return nil, ErrWebsocketListenerClosed
 	}
 	}
@@ -80,8 +74,12 @@ func (p *WebsocketListener) Close() error {
 	return p.server.Close()
 	return p.server.Close()
 }
 }
 
 
+func (p *WebsocketListener) Addr() net.Addr {
+	return p.ln.Addr()
+}
+
 // addr: domain:port
 // addr: domain:port
-func ConnectWebsocketServer(addr string) (Conn, error) {
+func ConnectWebsocketServer(addr string) (net.Conn, error) {
 	addr = "ws://" + addr + FrpWebsocketPath
 	addr = "ws://" + addr + FrpWebsocketPath
 	uri, err := url.Parse(addr)
 	uri, err := url.Parse(addr)
 	if err != nil {
 	if err != nil {
@@ -101,6 +99,5 @@ func ConnectWebsocketServer(addr string) (Conn, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	c := WrapConn(conn)
-	return c, nil
+	return conn, nil
 }
 }

+ 1 - 1
utils/version/version.go

@@ -19,7 +19,7 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-var version string = "0.29.0"
+var version string = "0.29.1"
 
 
 func Full() string {
 func Full() string {
 	return version
 	return version

+ 4 - 5
utils/vhost/https.go

@@ -17,11 +17,10 @@ package vhost
 import (
 import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"net"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	frpNet "github.com/fatedier/frp/utils/net"
-
 	gnet "github.com/fatedier/golib/net"
 	gnet "github.com/fatedier/golib/net"
 	"github.com/fatedier/golib/pool"
 	"github.com/fatedier/golib/pool"
 )
 )
@@ -48,7 +47,7 @@ type HttpsMuxer struct {
 	*VhostMuxer
 	*VhostMuxer
 }
 }
 
 
-func NewHttpsMuxer(listener frpNet.Listener, timeout time.Duration) (*HttpsMuxer, error) {
+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, timeout)
 	return &HttpsMuxer{mux}, err
 	return &HttpsMuxer{mux}, err
 }
 }
@@ -182,7 +181,7 @@ func readHandshake(rd io.Reader) (host string, err error) {
 	return
 	return
 }
 }
 
 
-func GetHttpsHostname(c frpNet.Conn) (_ frpNet.Conn, _ map[string]string, err error) {
+func GetHttpsHostname(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
 	reqInfoMap := make(map[string]string, 0)
 	reqInfoMap := make(map[string]string, 0)
 	sc, rd := gnet.NewSharedConn(c)
 	sc, rd := gnet.NewSharedConn(c)
 	host, err := readHandshake(rd)
 	host, err := readHandshake(rd)
@@ -191,5 +190,5 @@ func GetHttpsHostname(c frpNet.Conn) (_ frpNet.Conn, _ map[string]string, err er
 	}
 	}
 	reqInfoMap["Host"] = host
 	reqInfoMap["Host"] = host
 	reqInfoMap["Scheme"] = "https"
 	reqInfoMap["Scheme"] = "https"
-	return frpNet.WrapConn(sc), reqInfoMap, nil
+	return sc, reqInfoMap, nil
 }
 }

+ 28 - 23
utils/vhost/vhost.go

@@ -13,22 +13,25 @@
 package vhost
 package vhost
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
+	"net"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/fatedier/frp/utils/log"
 	"github.com/fatedier/frp/utils/log"
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
+	"github.com/fatedier/frp/utils/xlog"
 
 
 	"github.com/fatedier/golib/errors"
 	"github.com/fatedier/golib/errors"
 )
 )
 
 
-type muxFunc func(frpNet.Conn) (frpNet.Conn, map[string]string, error)
-type httpAuthFunc func(frpNet.Conn, string, string, string) (bool, error)
-type hostRewriteFunc func(frpNet.Conn, string) (frpNet.Conn, error)
+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 VhostMuxer struct {
 type VhostMuxer struct {
-	listener       frpNet.Listener
+	listener       net.Listener
 	timeout        time.Duration
 	timeout        time.Duration
 	vhostFunc      muxFunc
 	vhostFunc      muxFunc
 	authFunc       httpAuthFunc
 	authFunc       httpAuthFunc
@@ -36,7 +39,7 @@ type VhostMuxer struct {
 	registryRouter *VhostRouters
 	registryRouter *VhostRouters
 }
 }
 
 
-func NewVhostMuxer(listener frpNet.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
+func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
 	mux = &VhostMuxer{
 	mux = &VhostMuxer{
 		listener:       listener,
 		listener:       listener,
 		timeout:        timeout,
 		timeout:        timeout,
@@ -49,7 +52,7 @@ func NewVhostMuxer(listener frpNet.Listener, vhostFunc muxFunc, authFunc httpAut
 	return mux, nil
 	return mux, nil
 }
 }
 
 
-type CreateConnFunc func(remoteAddr string) (frpNet.Conn, error)
+type CreateConnFunc func(remoteAddr string) (net.Conn, error)
 
 
 // VhostRouteConfig is the params used to match HTTP requests
 // VhostRouteConfig is the params used to match HTTP requests
 type VhostRouteConfig struct {
 type VhostRouteConfig struct {
@@ -65,7 +68,7 @@ type VhostRouteConfig struct {
 
 
 // listen for a new domain name, if rewriteHost is not empty  and rewriteFunc is not nil
 // listen for a new domain name, if rewriteHost is not empty  and rewriteFunc is not nil
 // then rewrite the host header to rewriteHost
 // then rewrite the host header to rewriteHost
-func (v *VhostMuxer) Listen(cfg *VhostRouteConfig) (l *Listener, err error) {
+func (v *VhostMuxer) Listen(ctx context.Context, cfg *VhostRouteConfig) (l *Listener, err error) {
 	l = &Listener{
 	l = &Listener{
 		name:        cfg.Domain,
 		name:        cfg.Domain,
 		location:    cfg.Location,
 		location:    cfg.Location,
@@ -73,8 +76,8 @@ func (v *VhostMuxer) Listen(cfg *VhostRouteConfig) (l *Listener, err error) {
 		userName:    cfg.Username,
 		userName:    cfg.Username,
 		passWord:    cfg.Password,
 		passWord:    cfg.Password,
 		mux:         v,
 		mux:         v,
-		accept:      make(chan frpNet.Conn),
-		Logger:      log.NewPrefixLogger(""),
+		accept:      make(chan net.Conn),
+		ctx:         ctx,
 	}
 	}
 	err = v.registryRouter.Add(cfg.Domain, cfg.Location, l)
 	err = v.registryRouter.Add(cfg.Domain, cfg.Location, l)
 	if err != nil {
 	if err != nil {
@@ -123,7 +126,7 @@ func (v *VhostMuxer) run() {
 	}
 	}
 }
 }
 
 
-func (v *VhostMuxer) handle(c frpNet.Conn) {
+func (v *VhostMuxer) handle(c net.Conn) {
 	if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil {
 	if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil {
 		c.Close()
 		c.Close()
 		return
 		return
@@ -146,13 +149,14 @@ func (v *VhostMuxer) handle(c frpNet.Conn) {
 		c.Close()
 		c.Close()
 		return
 		return
 	}
 	}
+	xl := xlog.FromContextSafe(l.ctx)
 
 
 	// if authFunc is exist and userName/password is set
 	// if authFunc is exist and userName/password is set
 	// then verify user access
 	// then verify user access
 	if l.mux.authFunc != nil && l.userName != "" && l.passWord != "" {
 	if l.mux.authFunc != nil && l.userName != "" && l.passWord != "" {
 		bAccess, err := l.mux.authFunc(c, l.userName, l.passWord, reqInfoMap["Authorization"])
 		bAccess, err := l.mux.authFunc(c, l.userName, l.passWord, reqInfoMap["Authorization"])
 		if bAccess == false || err != nil {
 		if bAccess == false || err != nil {
-			l.Debug("check http Authorization failed")
+			xl.Debug("check http Authorization failed")
 			res := noAuthResponse()
 			res := noAuthResponse()
 			res.Write(c)
 			res.Write(c)
 			c.Close()
 			c.Close()
@@ -166,12 +170,12 @@ func (v *VhostMuxer) handle(c frpNet.Conn) {
 	}
 	}
 	c = sConn
 	c = sConn
 
 
-	l.Debug("get new http request host [%s] path [%s]", name, path)
+	xl.Debug("get new http request host [%s] path [%s]", name, path)
 	err = errors.PanicToError(func() {
 	err = errors.PanicToError(func() {
 		l.accept <- c
 		l.accept <- c
 	})
 	})
 	if err != nil {
 	if err != nil {
-		l.Warn("listener is already closed, ignore this request")
+		xl.Warn("listener is already closed, ignore this request")
 	}
 	}
 }
 }
 
 
@@ -182,11 +186,12 @@ type Listener struct {
 	userName    string
 	userName    string
 	passWord    string
 	passWord    string
 	mux         *VhostMuxer // for closing VhostMuxer
 	mux         *VhostMuxer // for closing VhostMuxer
-	accept      chan frpNet.Conn
-	log.Logger
+	accept      chan net.Conn
+	ctx         context.Context
 }
 }
 
 
-func (l *Listener) Accept() (frpNet.Conn, error) {
+func (l *Listener) Accept() (net.Conn, error) {
+	xl := xlog.FromContextSafe(l.ctx)
 	conn, ok := <-l.accept
 	conn, ok := <-l.accept
 	if !ok {
 	if !ok {
 		return nil, fmt.Errorf("Listener closed")
 		return nil, fmt.Errorf("Listener closed")
@@ -198,17 +203,13 @@ func (l *Listener) Accept() (frpNet.Conn, error) {
 	if l.mux.rewriteFunc != nil {
 	if l.mux.rewriteFunc != nil {
 		sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost)
 		sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost)
 		if err != nil {
 		if err != nil {
-			l.Warn("host header rewrite failed: %v", err)
+			xl.Warn("host header rewrite failed: %v", err)
 			return nil, fmt.Errorf("host header rewrite failed")
 			return nil, fmt.Errorf("host header rewrite failed")
 		}
 		}
-		l.Debug("rewrite host to [%s] success", l.rewriteHost)
+		xl.Debug("rewrite host to [%s] success", l.rewriteHost)
 		conn = sConn
 		conn = sConn
 	}
 	}
-
-	for _, prefix := range l.GetAllPrefix() {
-		conn.AddLogPrefix(prefix)
-	}
-	return conn, nil
+	return frpNet.NewContextConn(conn, l.ctx), nil
 }
 }
 
 
 func (l *Listener) Close() error {
 func (l *Listener) Close() error {
@@ -220,3 +221,7 @@ func (l *Listener) Close() error {
 func (l *Listener) Name() string {
 func (l *Listener) Name() string {
 	return l.name
 	return l.name
 }
 }
+
+func (l *Listener) Addr() net.Addr {
+	return (*net.TCPAddr)(nil)
+}

+ 42 - 0
utils/xlog/ctx.go

@@ -0,0 +1,42 @@
+// 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 xlog
+
+import (
+	"context"
+)
+
+type key int
+
+const (
+	xlogKey key = 0
+)
+
+func NewContext(ctx context.Context, xl *Logger) context.Context {
+	return context.WithValue(ctx, xlogKey, xl)
+}
+
+func FromContext(ctx context.Context) (xl *Logger, ok bool) {
+	xl, ok = ctx.Value(xlogKey).(*Logger)
+	return
+}
+
+func FromContextSafe(ctx context.Context) *Logger {
+	xl, ok := ctx.Value(xlogKey).(*Logger)
+	if !ok {
+		xl = New()
+	}
+	return xl
+}

+ 73 - 0
utils/xlog/xlog.go

@@ -0,0 +1,73 @@
+// 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 xlog
+
+import (
+	"github.com/fatedier/frp/utils/log"
+)
+
+// Logger is not thread safety for operations on prefix
+type Logger struct {
+	prefixes []string
+
+	prefixString string
+}
+
+func New() *Logger {
+	return &Logger{
+		prefixes: make([]string, 0),
+	}
+}
+
+func (l *Logger) ResetPrefixes() (old []string) {
+	old = l.prefixes
+	l.prefixes = make([]string, 0)
+	l.prefixString = ""
+	return
+}
+
+func (l *Logger) AppendPrefix(prefix string) *Logger {
+	l.prefixes = append(l.prefixes, prefix)
+	l.prefixString += "[" + prefix + "] "
+	return l
+}
+
+func (l *Logger) Spawn() *Logger {
+	nl := New()
+	for _, v := range l.prefixes {
+		nl.AppendPrefix(v)
+	}
+	return nl
+}
+
+func (l *Logger) Error(format string, v ...interface{}) {
+	log.Log.Error(l.prefixString+format, v...)
+}
+
+func (l *Logger) Warn(format string, v ...interface{}) {
+	log.Log.Warn(l.prefixString+format, v...)
+}
+
+func (l *Logger) Info(format string, v ...interface{}) {
+	log.Log.Info(l.prefixString+format, v...)
+}
+
+func (l *Logger) Debug(format string, v ...interface{}) {
+	log.Log.Debug(l.prefixString+format, v...)
+}
+
+func (l *Logger) Trace(format string, v ...interface{}) {
+	log.Log.Trace(l.prefixString+format, v...)
+}