Skip to content

Commit 243ca99

Browse files
authored
Merge pull request #4324 from fatedier/dev
bump version
2 parents e649692 + b4d5d8c commit 243ca99

17 files changed

+283
-15
lines changed

README.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,24 @@
99

1010
<h3 align="center">Gold Sponsors</h3>
1111
<!--gold sponsors start-->
12+
<p align="center">
13+
<a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
14+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
15+
</a>
16+
</p>
1217
<p align="center">
1318
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
14-
<img width="350px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
19+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
1520
</a>
16-
<a>&nbsp</a>
21+
</p>
22+
<p align="center">
1723
<a href="https://github.com/daytonaio/daytona" target="_blank">
18-
<img width="360px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
24+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
25+
</a>
26+
</p>
27+
<p align="center">
28+
<a href="https://github.com/beclab/terminus" target="_blank">
29+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_terminusos.jpeg">
1930
</a>
2031
</p>
2132
<!--gold sponsors end-->

README_zh.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,24 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP
1111

1212
<h3 align="center">Gold Sponsors</h3>
1313
<!--gold sponsors start-->
14+
<p align="center">
15+
<a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
16+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
17+
</a>
18+
</p>
1419
<p align="center">
1520
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
16-
<img width="350px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
21+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
1722
</a>
18-
<a>&nbsp</a>
23+
</p>
24+
<p align="center">
1925
<a href="https://github.com/daytonaio/daytona" target="_blank">
20-
<img width="360px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
26+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
27+
</a>
28+
</p>
29+
<p align="center">
30+
<a href="https://github.com/beclab/terminus" target="_blank">
31+
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_terminusos.jpeg">
2132
</a>
2233
</p>
2334
<!--gold sponsors end-->

Release.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
### Fixes
1+
### Features
22

3-
* Fixed an issue where HTTP/2 was not enabled for https2http and https2https plugins.
4-
* Fixed the issue where the default values of INI configuration parameters are inconsistent with other configuration formats.
3+
* Added a new plugin "http2http" which allows forwarding HTTP requests to another HTTP server, supporting options like local address binding, host header rewrite, and custom request headers.
4+
* Added `enableHTTP2` option to control whether to enable HTTP/2 in plugin https2http and https2https, default is true.
55

66
### Changes
77

8-
* Updated the default value of `transport.tcpMuxKeepaliveInterval` from 60 to 30.
9-
* On the Android platform, the Google DNS server is used only when the default DNS server cannot be obtained.
8+
* Plugin https2http & https2https: return 421 `Misdirected Request` if host not match sni.

conf/frpc_full_example.toml

+10
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,16 @@ localAddr = "127.0.0.1:443"
315315
hostHeaderRewrite = "127.0.0.1"
316316
requestHeaders.set.x-from-where = "frp"
317317

318+
[[proxies]]
319+
name = "plugin_http2http"
320+
type = "tcp"
321+
remotePort = 6007
322+
[proxies.plugin]
323+
type = "http2http"
324+
localAddr = "127.0.0.1:80"
325+
hostHeaderRewrite = "127.0.0.1"
326+
requestHeaders.set.x-from-where = "frp"
327+
318328
[[proxies]]
319329
name = "secret_tcp"
320330
# If the type is secret tcp, remotePort is useless

doc/pic/donate-wechatpay.png

-26.9 KB
Binary file not shown.

doc/pic/sponsor_daytona.png

-14.6 KB
Loading

doc/pic/sponsor_doppler.png

-40.9 KB
Binary file not shown.

doc/pic/sponsor_lokal.png

55.1 KB
Loading

doc/pic/sponsor_nango.png

-11.8 KB
Binary file not shown.

doc/pic/sponsor_terminusos.jpeg

30.7 KB
Loading

pkg/config/v1/plugin.go

+38-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,15 @@ import (
2020
"errors"
2121
"fmt"
2222
"reflect"
23+
24+
"github.com/samber/lo"
25+
26+
"github.com/fatedier/frp/pkg/util/util"
2327
)
2428

25-
type ClientPluginOptions interface{}
29+
type ClientPluginOptions interface {
30+
Complete()
31+
}
2632

2733
type TypedClientPluginOptions struct {
2834
Type string `json:"type"`
@@ -73,6 +79,7 @@ const (
7379
PluginHTTPProxy = "http_proxy"
7480
PluginHTTPS2HTTP = "https2http"
7581
PluginHTTPS2HTTPS = "https2https"
82+
PluginHTTP2HTTP = "http2http"
7683
PluginSocks5 = "socks5"
7784
PluginStaticFile = "static_file"
7885
PluginUnixDomainSocket = "unix_domain_socket"
@@ -83,6 +90,7 @@ var clientPluginOptionsTypeMap = map[string]reflect.Type{
8390
PluginHTTPProxy: reflect.TypeOf(HTTPProxyPluginOptions{}),
8491
PluginHTTPS2HTTP: reflect.TypeOf(HTTPS2HTTPPluginOptions{}),
8592
PluginHTTPS2HTTPS: reflect.TypeOf(HTTPS2HTTPSPluginOptions{}),
93+
PluginHTTP2HTTP: reflect.TypeOf(HTTP2HTTPPluginOptions{}),
8694
PluginSocks5: reflect.TypeOf(Socks5PluginOptions{}),
8795
PluginStaticFile: reflect.TypeOf(StaticFilePluginOptions{}),
8896
PluginUnixDomainSocket: reflect.TypeOf(UnixDomainSocketPluginOptions{}),
@@ -95,36 +103,61 @@ type HTTP2HTTPSPluginOptions struct {
95103
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
96104
}
97105

106+
func (o *HTTP2HTTPSPluginOptions) Complete() {}
107+
98108
type HTTPProxyPluginOptions struct {
99109
Type string `json:"type,omitempty"`
100110
HTTPUser string `json:"httpUser,omitempty"`
101111
HTTPPassword string `json:"httpPassword,omitempty"`
102112
}
103113

114+
func (o *HTTPProxyPluginOptions) Complete() {}
115+
104116
type HTTPS2HTTPPluginOptions struct {
105117
Type string `json:"type,omitempty"`
106118
LocalAddr string `json:"localAddr,omitempty"`
107119
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
108120
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
121+
EnableHTTP2 *bool `json:"enableHTTP2,omitempty"`
109122
CrtPath string `json:"crtPath,omitempty"`
110123
KeyPath string `json:"keyPath,omitempty"`
111124
}
112125

126+
func (o *HTTPS2HTTPPluginOptions) Complete() {
127+
o.EnableHTTP2 = util.EmptyOr(o.EnableHTTP2, lo.ToPtr(true))
128+
}
129+
113130
type HTTPS2HTTPSPluginOptions struct {
114131
Type string `json:"type,omitempty"`
115132
LocalAddr string `json:"localAddr,omitempty"`
116133
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
117134
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
135+
EnableHTTP2 *bool `json:"enableHTTP2,omitempty"`
118136
CrtPath string `json:"crtPath,omitempty"`
119137
KeyPath string `json:"keyPath,omitempty"`
120138
}
121139

140+
func (o *HTTPS2HTTPSPluginOptions) Complete() {
141+
o.EnableHTTP2 = util.EmptyOr(o.EnableHTTP2, lo.ToPtr(true))
142+
}
143+
144+
type HTTP2HTTPPluginOptions struct {
145+
Type string `json:"type,omitempty"`
146+
LocalAddr string `json:"localAddr,omitempty"`
147+
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
148+
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
149+
}
150+
151+
func (o *HTTP2HTTPPluginOptions) Complete() {}
152+
122153
type Socks5PluginOptions struct {
123154
Type string `json:"type,omitempty"`
124155
Username string `json:"username,omitempty"`
125156
Password string `json:"password,omitempty"`
126157
}
127158

159+
func (o *Socks5PluginOptions) Complete() {}
160+
128161
type StaticFilePluginOptions struct {
129162
Type string `json:"type,omitempty"`
130163
LocalPath string `json:"localPath,omitempty"`
@@ -133,7 +166,11 @@ type StaticFilePluginOptions struct {
133166
HTTPPassword string `json:"httpPassword,omitempty"`
134167
}
135168

169+
func (o *StaticFilePluginOptions) Complete() {}
170+
136171
type UnixDomainSocketPluginOptions struct {
137172
Type string `json:"type,omitempty"`
138173
UnixPath string `json:"unixPath,omitempty"`
139174
}
175+
176+
func (o *UnixDomainSocketPluginOptions) Complete() {}

pkg/config/v1/proxy.go

+4
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ func (c *ProxyBaseConfig) Complete(namePrefix string) {
127127
c.Name = lo.Ternary(namePrefix == "", "", namePrefix+".") + c.Name
128128
c.LocalIP = util.EmptyOr(c.LocalIP, "127.0.0.1")
129129
c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient)
130+
131+
if c.Plugin.ClientPluginOptions != nil {
132+
c.Plugin.ClientPluginOptions.Complete()
133+
}
130134
}
131135

132136
func (c *ProxyBaseConfig) MarshalToMsg(m *msg.NewProxy) {

pkg/plugin/client/http2http.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2024 The frp Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package plugin
16+
17+
import (
18+
"io"
19+
stdlog "log"
20+
"net"
21+
"net/http"
22+
"net/http/httputil"
23+
24+
"github.com/fatedier/golib/pool"
25+
26+
v1 "github.com/fatedier/frp/pkg/config/v1"
27+
"github.com/fatedier/frp/pkg/util/log"
28+
netpkg "github.com/fatedier/frp/pkg/util/net"
29+
)
30+
31+
func init() {
32+
Register(v1.PluginHTTP2HTTP, NewHTTP2HTTPPlugin)
33+
}
34+
35+
type HTTP2HTTPPlugin struct {
36+
opts *v1.HTTP2HTTPPluginOptions
37+
38+
l *Listener
39+
s *http.Server
40+
}
41+
42+
func NewHTTP2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
43+
opts := options.(*v1.HTTP2HTTPPluginOptions)
44+
45+
listener := NewProxyListener()
46+
47+
p := &HTTP2HTTPPlugin{
48+
opts: opts,
49+
l: listener,
50+
}
51+
52+
rp := &httputil.ReverseProxy{
53+
Rewrite: func(r *httputil.ProxyRequest) {
54+
req := r.Out
55+
req.URL.Scheme = "http"
56+
req.URL.Host = p.opts.LocalAddr
57+
if p.opts.HostHeaderRewrite != "" {
58+
req.Host = p.opts.HostHeaderRewrite
59+
}
60+
for k, v := range p.opts.RequestHeaders.Set {
61+
req.Header.Set(k, v)
62+
}
63+
},
64+
BufferPool: pool.NewBuffer(32 * 1024),
65+
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
66+
}
67+
68+
p.s = &http.Server{
69+
Handler: rp,
70+
ReadHeaderTimeout: 0,
71+
}
72+
73+
go func() {
74+
_ = p.s.Serve(listener)
75+
}()
76+
77+
return p, nil
78+
}
79+
80+
func (p *HTTP2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
81+
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
82+
_ = p.l.PutConn(wrapConn)
83+
}
84+
85+
func (p *HTTP2HTTPPlugin) Name() string {
86+
return v1.PluginHTTP2HTTP
87+
}
88+
89+
func (p *HTTP2HTTPPlugin) Close() error {
90+
return p.s.Close()
91+
}

pkg/plugin/client/https2http.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ import (
2727
"time"
2828

2929
"github.com/fatedier/golib/pool"
30+
"github.com/samber/lo"
3031

3132
v1 "github.com/fatedier/frp/pkg/config/v1"
3233
"github.com/fatedier/frp/pkg/transport"
34+
httppkg "github.com/fatedier/frp/pkg/util/http"
3335
"github.com/fatedier/frp/pkg/util/log"
3436
netpkg "github.com/fatedier/frp/pkg/util/net"
3537
)
@@ -71,6 +73,17 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
7173
BufferPool: pool.NewBuffer(32 * 1024),
7274
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
7375
}
76+
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
77+
if r.TLS != nil {
78+
tlsServerName, _ := httppkg.CanonicalHost(r.TLS.ServerName)
79+
host, _ := httppkg.CanonicalHost(r.Host)
80+
if tlsServerName != "" && tlsServerName != host {
81+
w.WriteHeader(http.StatusMisdirectedRequest)
82+
return
83+
}
84+
}
85+
rp.ServeHTTP(w, r)
86+
})
7487

7588
var (
7689
tlsConfig *tls.Config
@@ -87,10 +100,13 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
87100
}
88101

89102
p.s = &http.Server{
90-
Handler: rp,
103+
Handler: handler,
91104
ReadHeaderTimeout: 60 * time.Second,
92105
TLSConfig: tlsConfig,
93106
}
107+
if !lo.FromPtr(opts.EnableHTTP2) {
108+
p.s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
109+
}
94110

95111
go func() {
96112
_ = p.s.ServeTLS(listener, "", "")

pkg/plugin/client/https2https.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ import (
2727
"time"
2828

2929
"github.com/fatedier/golib/pool"
30+
"github.com/samber/lo"
3031

3132
v1 "github.com/fatedier/frp/pkg/config/v1"
3233
"github.com/fatedier/frp/pkg/transport"
34+
httppkg "github.com/fatedier/frp/pkg/util/http"
3335
"github.com/fatedier/frp/pkg/util/log"
3436
netpkg "github.com/fatedier/frp/pkg/util/net"
3537
)
@@ -77,6 +79,17 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
7779
BufferPool: pool.NewBuffer(32 * 1024),
7880
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
7981
}
82+
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
83+
if r.TLS != nil {
84+
tlsServerName, _ := httppkg.CanonicalHost(r.TLS.ServerName)
85+
host, _ := httppkg.CanonicalHost(r.Host)
86+
if tlsServerName != "" && tlsServerName != host {
87+
w.WriteHeader(http.StatusMisdirectedRequest)
88+
return
89+
}
90+
}
91+
rp.ServeHTTP(w, r)
92+
})
8093

8194
var (
8295
tlsConfig *tls.Config
@@ -93,10 +106,13 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
93106
}
94107

95108
p.s = &http.Server{
96-
Handler: rp,
109+
Handler: handler,
97110
ReadHeaderTimeout: 60 * time.Second,
98111
TLSConfig: tlsConfig,
99112
}
113+
if !lo.FromPtr(opts.EnableHTTP2) {
114+
p.s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
115+
}
100116

101117
go func() {
102118
_ = p.s.ServeTLS(listener, "", "")

pkg/util/version/version.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
package version
1616

17-
var version = "0.58.1"
17+
var version = "0.59.0"
1818

1919
func Full() string {
2020
return version

0 commit comments

Comments
 (0)