Skip to content

Commit

Permalink
Merge pull request #201 from fatedier/dev
Browse files Browse the repository at this point in the history
bump version to 0.9.1
  • Loading branch information
fatedier authored Dec 27, 2016
2 parents dde734c + 34b98dd commit 044bb69
Show file tree
Hide file tree
Showing 19 changed files with 397 additions and 92 deletions.
36 changes: 32 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

## What is frp?

frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. Now, it supports tcp, http and https protocol when requests can be forwarded by domains to backward web services.
frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. Now, it supports tcp, udp, http and https protocol when requests can be forwarded by domains to backward web services.

## Catalog
## Table of Contents

<!-- vim-markdown-toc GFM -->
* [What can I do with frp?](#what-can-i-do-with-frp)
Expand All @@ -29,6 +29,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
* [Rewriting the Host Header](#rewriting-the-host-header)
* [Password protecting your web service](#password-protecting-your-web-service)
* [Custom subdomain names](#custom-subdomain-names)
* [URL routing](#url-routing)
* [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
* [Development Plan](#development-plan)
* [Contributing](#contributing)
Expand Down Expand Up @@ -108,7 +109,7 @@ Put **frpc** and **frpc.ini** to your server in LAN.
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.

Howerver, we can expose a http or https service using frp.
However, we can expose a http or https service using frp.

1. Modify frps.ini, configure a http reverse proxy named [web] and set http port as 8080, custom domain as `www.yourdomain.com`:

Expand Down Expand Up @@ -238,7 +239,7 @@ use_gzip = true

### Reload configures without frps stopped

If your want to add a new reverse proxy and avoid restarting frps, you can use this function:
If you want to add a new reverse proxy and avoid restarting frps, you can use this function:

1. `dashboard_port` should be set in frps.ini:

Expand Down Expand Up @@ -414,6 +415,30 @@ Now you can visit your web service by host `test.frps.com`.

Note that if `subdomain_host` is not empty, `custom_domains` should not be the subdomain of `subdomain_host`.

### URL routing

frp support forward http requests to different backward 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.

```ini
# frpc.ini
[web01]
privilege_mode = true
type = http
local_port = 80
custom_domains = web.yourdomain.com
locations = /

[web02]
privilege_mode = true
type = http
local_port = 81
custom_domains = web.yourdomain.com
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

frpc can connect frps using HTTP PROXY if you set os environment `HTTP_PROXY` or configure `http_proxy` param in frpc.ini file.
Expand Down Expand Up @@ -452,6 +477,8 @@ Interested in getting involved? We would like to help you!

If frp help you a lot, you can support us by:

frp QQ group: 606194980

### AliPay

![donation-alipay](/doc/pic/donate-alipay.png)
Expand All @@ -469,3 +496,4 @@ Donate money by [paypal](https://www.paypal.me/fatedier) to my account **fatedie
* [Eric Larssen](https://github.com/ericlarssen)
* [Damon Zhao](https://github.com/se77en)
* [Manfred Touron](https://github.com/moul)
* [xuebing1110](https://github.com/xuebing1110)
31 changes: 30 additions & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[README](README.md) | [中文文档](README_zh.md)

frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内网穿透,对外网提供服务,支持 tcp, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。
frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内网穿透,对外网提供服务,支持 tcp, udp, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。

## 目录

Expand All @@ -27,6 +27,7 @@ frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内
* [修改 Host Header](#修改-host-header)
* [通过密码保护你的 web 服务](#通过密码保护你的-web-服务)
* [自定义二级域名](#自定义二级域名)
* [URL 路由](#url-路由)
* [通过 HTTP PROXY 连接 frps](#通过-http-proxy-连接-frps)
* [开发计划](#开发计划)
* [为 frp 做贡献](#为-frp-做贡献)
Expand Down Expand Up @@ -426,6 +427,31 @@ frps 和 fprc 都启动成功后,通过 `test.frps.com` 就可以访问到内

同一个 http 或 https 类型的代理中 `custom_domains``subdomain` 可以同时配置。

### URL 路由

frp 支持根据请求的 URL 路径路由转发到不同的后端服务。

通过配置文件中的 `locations` 字段指定一个或多个 proxy 能够匹配的 URL 前缀(目前仅支持最大前缀匹配,之后会考虑正则匹配)。例如指定 `locations = /news`,则所有 URL 以 `/news` 开头的请求都会被转发到这个服务。

```ini
# frpc.ini
[web01]
privilege_mode = true
type = http
local_port = 80
custom_domains = web.yourdomain.com
locations = /

[web02]
privilege_mode = true
type = http
local_port = 81
custom_domains = web.yourdomain.com
locations = /news,/about
```

按照上述的示例配置后,`web.yourdomain.com` 这个域名下所有以 `/news` 以及 `/about` 作为前缀的 URL 请求都会被转发到 web02,其余的请求会被转发到 web01。

### 通过 HTTP PROXY 连接 frps

在只能通过代理访问外网的环境内,frpc 支持通过 HTTP PROXY 和 frps 进行通信。
Expand Down Expand Up @@ -470,6 +496,8 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进

如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。

frp 交流群:606194980 (QQ 群号)

### 支付宝扫码捐赠

![donate-alipay](/doc/pic/donate-alipay.png)
Expand All @@ -487,3 +515,4 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
* [Eric Larssen](https://github.com/ericlarssen)
* [Damon Zhao](https://github.com/se77en)
* [Manfred Touron](https://github.com/moul)
* [xuebing1110](https://github.com/xuebing1110)
7 changes: 4 additions & 3 deletions conf/frpc.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ type = tcp
local_ip = 127.0.0.1
local_port = 22
# true or false, if true, messages between frps and frpc will be encrypted, default is false
use_encryption = true
use_encryption = false
# default is false
use_gzip = false
# connections will be established in advance, default value is zero
pool_count = 10

[dns]
type = udp
Expand All @@ -47,6 +45,7 @@ type = http
local_ip = 127.0.0.1
local_port = 80
use_gzip = true
# connections will be established in advance, default value is zero
pool_count = 20
# http username and password are safety certification for http protocol
# if not set, you can access this custom_domains without certification
Expand Down Expand Up @@ -77,5 +76,7 @@ local_ip = 127.0.0.1
local_port = 80
use_gzip = true
custom_domains = web03.yourdomain.com
# locations is only useful for http type
locations = /,/pic
host_header_rewrite = example.com
subdomain = dev
9 changes: 8 additions & 1 deletion src/assets/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<th class="tab_info" ng-click="col='current_conns';desc=!desc">CurCon<i class="iconfont pull-right">&#xe66d;</i></th>
<th class="tab_info" ng-click="col='daily[daily.length-1].flow_out';desc=!desc">FlowOut<i class="iconfont pull-right">&#xe66d;</i></th>
<th class="tab_info" ng-click="col='daily[daily.length-1].flow_in';desc=!desc">FlowIn<i class="iconfont pull-right">&#xe66d;</i></th>
<th class="tab_info" ng-click="col='daily[daily.length-1].total_accept_conns';desc=!desc">TotalAcceptConns<i class="iconfont pull-right">&#xe66d;</i></th>
</tr>
</thead>
<tbody id="tab_body">
Expand All @@ -38,6 +39,7 @@
<td><span ng-bind="x.current_conns"></span></td>
<td><span ng-bind="x.daily[x.daily.length-1].flow_out"></span></td>
<td><span ng-bind="x.daily[x.daily.length-1].flow_in"></span></td>
<td><span ng-bind="x.daily[x.daily.length-1].total_accept_conns"></span></td>
</tr>
</tbody>
</table>
Expand All @@ -63,7 +65,8 @@
listen_port: "<<< .ListenPort >>>",
current_conns: <<< .CurrentConns >>> ,
domains: [ <<< range.CustomDomains >>> "<<< . >>>", <<< end >>> ],
stat: "<<< .Status >>>",
locations: [ <<< range.Locations >>> "<<< . >>>", <<< end >>> ],
stat: "<<< .Status>>>",
use_encryption: "<<< .UseEncryption >>>",
use_gzip: "<<< .UseGzip >>>",
privilege_mode: "<<< .PrivilegeMode >>>",
Expand Down Expand Up @@ -222,6 +225,10 @@
newrow += "<tr class='info_detail'><td colspan='4'>Domains</td><td colspan='4'>" +
alldata[index].domains[domainindex] + "</td><tr>";
}
for (var locindex in alldata[index].locations) {
newrow += "<tr class='info_detail'><td colspan='4'>Locations</td><td colspan='4'>" +
alldata[index].locations[locindex] + "</td><tr>";
}
newrow += "<tr class='info_detail'><td colspan='4'>Ip</td><td colspan='4'>" + alldata[index].bind_addr + "</td><tr>";
newrow += "<tr class='info_detail'><td colspan='4'>Status</td><td colspan='4'>" + alldata[index].stat + "</td><tr>";
newrow += "<tr class='info_detail'><td colspan='4'>Encryption</td><td colspan='4'>" + alldata[index].use_encryption + "</td><tr>";
Expand Down
2 changes: 1 addition & 1 deletion src/assets/statik/statik.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/cmd/frpc/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) {
privilegeKey := pcrypto.GetAuthKey(cli.Name + client.PrivilegeToken + fmt.Sprintf("%d", nowTime))
req.RemotePort = cli.RemotePort
req.CustomDomains = cli.CustomDomains
req.Locations = cli.Locations
req.PrivilegeKey = privilegeKey
} else {
authKey := pcrypto.GetAuthKey(cli.Name + cli.AuthToken + fmt.Sprintf("%d", nowTime))
Expand Down
18 changes: 5 additions & 13 deletions src/cmd/frps/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func controlWorker(c *conn.Conn) {
}

// login when type is NewCtlConn or NewWorkConn
ret, info := doLogin(cliReq, c)
ret, info, s := doLogin(cliReq, c)
// if login type is NewWorkConn, nothing will be send to frpc
if cliReq.Type == consts.NewCtlConn {
cliRes := &msg.ControlRes{
Expand All @@ -94,12 +94,6 @@ func controlWorker(c *conn.Conn) {
return
}

s, ok := server.GetProxyServer(cliReq.ProxyName)
if !ok {
log.Warn("ProxyName [%s] does not exist now", cliReq.ProxyName)
return
}

// create a channel for sending messages
msgSendChan := make(chan interface{}, 1024)
go msgSender(s, c, msgSendChan)
Expand Down Expand Up @@ -199,7 +193,7 @@ func msgSender(s *server.ProxyServer, c *conn.Conn, msgSendChan chan interface{}
// NewCtlConn
// NewWorkConn
// NewWorkConnUdp
func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string, s *server.ProxyServer) {
ret = 1
// check if PrivilegeMode is enabled
if req.PrivilegeMode && !server.PrivilegeMode {
Expand All @@ -208,10 +202,7 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
return
}

var (
s *server.ProxyServer
ok bool
)
var ok bool
s, ok = server.GetProxyServer(req.ProxyName)
if req.PrivilegeMode && req.Type == consts.NewCtlConn {
log.Debug("ProxyName [%s], doLogin and privilege mode is enabled", req.ProxyName)
Expand Down Expand Up @@ -297,6 +288,7 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
}

// set infomations from frpc
s.BindAddr = server.BindAddr
s.UseEncryption = req.UseEncryption
s.UseGzip = req.UseGzip
s.HostHeaderRewrite = req.HostHeaderRewrite
Expand Down Expand Up @@ -332,7 +324,7 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
}

// update metric's proxy status
metric.SetProxyInfo(s.Name, s.Type, s.BindAddr, s.UseEncryption, s.UseGzip, s.PrivilegeMode, s.CustomDomains, s.ListenPort)
metric.SetProxyInfo(s.Name, s.Type, s.BindAddr, s.UseEncryption, s.UseGzip, s.PrivilegeMode, s.CustomDomains, s.Locations, s.ListenPort)

// start proxy and listen for user connections, no block
err := s.Start(c)
Expand Down
1 change: 1 addition & 0 deletions src/models/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type ProxyClient struct {

RemotePort int64
CustomDomains []string
Locations []string

udpTunnel *conn.Conn
once sync.Once
Expand Down
11 changes: 11 additions & 0 deletions src/models/client/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ func LoadConf(confFile string) (err error) {
if ok {
proxyClient.HttpPassWord = tmpStr
}

}
if proxyClient.Type == "http" || proxyClient.Type == "https" {
// subdomain
tmpStr, ok = section["subdomain"]
if ok {
Expand Down Expand Up @@ -227,6 +230,14 @@ func LoadConf(confFile string) (err error) {
if !ok && proxyClient.SubDomain == "" {
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them when type is http", proxyClient.Name)
}

// locations
locations, ok := section["locations"]
if ok {
proxyClient.Locations = strings.Split(locations, ",")
} else {
proxyClient.Locations = []string{""}
}
} else if proxyClient.Type == "https" {
// custom_domains
domainStr, ok := section["custom_domains"]
Expand Down
4 changes: 3 additions & 1 deletion src/models/metric/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type ServerMetric struct {
BindAddr string `json:"bind_addr"`
ListenPort int64 `json:"listen_port"`
CustomDomains []string `json:"custom_domains"`
Locations []string `json:"locations"`
Status string `json:"status"`
UseEncryption bool `json:"use_encryption"`
UseGzip bool `json:"use_gzip"`
Expand Down Expand Up @@ -112,7 +113,7 @@ func GetProxyMetrics(proxyName string) *ServerMetric {

func SetProxyInfo(proxyName string, proxyType, bindAddr string,
useEncryption, useGzip, privilegeMode bool, customDomains []string,
listenPort int64) {
locations []string, listenPort int64) {
smMutex.Lock()
info, ok := ServerMetricInfoMap[proxyName]
if !ok {
Expand All @@ -127,6 +128,7 @@ func SetProxyInfo(proxyName string, proxyType, bindAddr string,
info.BindAddr = bindAddr
info.ListenPort = listenPort
info.CustomDomains = customDomains
info.Locations = locations
ServerMetricInfoMap[proxyName] = info
smMutex.Unlock()
}
Expand Down
1 change: 1 addition & 0 deletions src/models/msg/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type ControlReq struct {
ProxyType string `json:"proxy_type"`
RemotePort int64 `json:"remote_port"`
CustomDomains []string `json:"custom_domains, omitempty"`
Locations []string `json:"locations"`
HostHeaderRewrite string `json:"host_header_rewrite"`
HttpUserName string `json:"http_username"`
HttpPassWord string `json:"http_password"`
Expand Down
14 changes: 11 additions & 3 deletions src/models/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,14 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e
} else {
return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type is http", proxyServer.Name)
}

// locations
locations, ok := section["locations"]
if ok {
proxyServer.Locations = strings.Split(locations, ",")
} else {
proxyServer.Locations = []string{""}
}
} else if proxyServer.Type == "https" {
// for https
proxyServer.ListenPort = VhostHttpsPort
Expand Down Expand Up @@ -332,7 +340,7 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e
// set metric statistics of all proxies
for name, p := range proxyServers {
metric.SetProxyInfo(name, p.Type, p.BindAddr, p.UseEncryption, p.UseGzip,
p.PrivilegeMode, p.CustomDomains, p.ListenPort)
p.PrivilegeMode, p.CustomDomains, p.Locations, p.ListenPort)
}
return proxyServers, nil
}
Expand Down Expand Up @@ -387,14 +395,14 @@ func CreateProxy(s *ProxyServer) error {
if oldServer.Status == consts.Working {
return fmt.Errorf("this proxy is already working now")
}
oldServer.Close()
oldServer.Release()
if oldServer.PrivilegeMode {
delete(ProxyServers, s.Name)
}
}
ProxyServers[s.Name] = s
metric.SetProxyInfo(s.Name, s.Type, s.BindAddr, s.UseEncryption, s.UseGzip,
s.PrivilegeMode, s.CustomDomains, s.ListenPort)
s.PrivilegeMode, s.CustomDomains, s.Locations, s.ListenPort)
s.Init()
return nil
}
Expand Down
Loading

0 comments on commit 044bb69

Please sign in to comment.