Compare commits

..

5 Commits

Author SHA1 Message Date
fatedier
496077db7e add http2http plugin config 2024-06-12 16:12:38 +08:00
fatedier
56c8fd2c4c add e2e tests for http2http client plugin 2024-06-12 15:07:01 +08:00
fatedier
561132f867 Add http2http plugin
Adds support for a new "http2http" plugin in the configuration and implementation.

- Introduces a new constant `PluginHTTP2HTTP` to represent the "http2http" plugin in the plugin configuration.
- Adds `HTTP2HTTPPluginOptions` struct to define the options available for the "http2http" plugin, including local address binding, host header rewrite, and custom request headers.
- Implements the `http2http` plugin in `http2http.go`, which includes the setup of a reverse proxy server that forwards HTTP requests to another HTTP server based on the specified plugin options.
- Registers the "http2http" plugin with the plugin system, allowing it to be utilized in client configurations.


---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/fatedier/frp/pull/4275?shareId=a866fdb3-e40a-4d40-9326-546dc4efce63).
2024-06-12 14:40:12 +08:00
fatedier
39295292f1 Add license information to newly added files
Adds license information to the newly created `pkg/plugin/client/http2http.go` file.

- **License Addition**: Incorporates Apache License 2.0 information at the top of the `http2http.go` file, aligning with the repository's licensing practices for source files.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/fatedier/frp/pull/4275?shareId=07b6614a-5cf5-4f9f-be18-0256c8aad313).
2024-06-12 12:03:15 +08:00
fatedier
13198a4d1d Add http2http client plugin with hostHeaderRewrite and requestHeaders support 2024-06-12 11:19:15 +08:00
12 changed files with 13 additions and 91 deletions

View File

@@ -9,11 +9,6 @@
<h3 align="center">Gold Sponsors</h3> <h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start--> <!--gold sponsors start-->
<p align="center">
<a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
</a>
</p>
<p align="center"> <p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank"> <a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png"> <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">

View File

@@ -11,11 +11,6 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP
<h3 align="center">Gold Sponsors</h3> <h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start--> <!--gold sponsors start-->
<p align="center">
<a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
</a>
</p>
<p align="center"> <p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank"> <a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png"> <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">

View File

@@ -1,8 +1,3 @@
### Features ### Features
* 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. * 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.
* Added `enableHTTP2` option to control whether to enable HTTP/2 in plugin https2http and https2https, default is true.
### Changes
* Plugin https2http & https2https: return 421 `Misdirected Request` if host not match sni.

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
doc/pic/sponsor_doppler.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

BIN
doc/pic/sponsor_nango.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -20,15 +20,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"github.com/samber/lo"
"github.com/fatedier/frp/pkg/util/util"
) )
type ClientPluginOptions interface { type ClientPluginOptions interface{}
Complete()
}
type TypedClientPluginOptions struct { type TypedClientPluginOptions struct {
Type string `json:"type"` Type string `json:"type"`
@@ -79,10 +73,10 @@ const (
PluginHTTPProxy = "http_proxy" PluginHTTPProxy = "http_proxy"
PluginHTTPS2HTTP = "https2http" PluginHTTPS2HTTP = "https2http"
PluginHTTPS2HTTPS = "https2https" PluginHTTPS2HTTPS = "https2https"
PluginHTTP2HTTP = "http2http"
PluginSocks5 = "socks5" PluginSocks5 = "socks5"
PluginStaticFile = "static_file" PluginStaticFile = "static_file"
PluginUnixDomainSocket = "unix_domain_socket" PluginUnixDomainSocket = "unix_domain_socket"
PluginHTTP2HTTP = "http2http"
) )
var clientPluginOptionsTypeMap = map[string]reflect.Type{ var clientPluginOptionsTypeMap = map[string]reflect.Type{
@@ -90,10 +84,10 @@ var clientPluginOptionsTypeMap = map[string]reflect.Type{
PluginHTTPProxy: reflect.TypeOf(HTTPProxyPluginOptions{}), PluginHTTPProxy: reflect.TypeOf(HTTPProxyPluginOptions{}),
PluginHTTPS2HTTP: reflect.TypeOf(HTTPS2HTTPPluginOptions{}), PluginHTTPS2HTTP: reflect.TypeOf(HTTPS2HTTPPluginOptions{}),
PluginHTTPS2HTTPS: reflect.TypeOf(HTTPS2HTTPSPluginOptions{}), PluginHTTPS2HTTPS: reflect.TypeOf(HTTPS2HTTPSPluginOptions{}),
PluginHTTP2HTTP: reflect.TypeOf(HTTP2HTTPPluginOptions{}),
PluginSocks5: reflect.TypeOf(Socks5PluginOptions{}), PluginSocks5: reflect.TypeOf(Socks5PluginOptions{}),
PluginStaticFile: reflect.TypeOf(StaticFilePluginOptions{}), PluginStaticFile: reflect.TypeOf(StaticFilePluginOptions{}),
PluginUnixDomainSocket: reflect.TypeOf(UnixDomainSocketPluginOptions{}), PluginUnixDomainSocket: reflect.TypeOf(UnixDomainSocketPluginOptions{}),
PluginHTTP2HTTP: reflect.TypeOf(HTTP2HTTPPluginOptions{}),
} }
type HTTP2HTTPSPluginOptions struct { type HTTP2HTTPSPluginOptions struct {
@@ -103,61 +97,36 @@ type HTTP2HTTPSPluginOptions struct {
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"` RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
} }
func (o *HTTP2HTTPSPluginOptions) Complete() {}
type HTTPProxyPluginOptions struct { type HTTPProxyPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
HTTPUser string `json:"httpUser,omitempty"` HTTPUser string `json:"httpUser,omitempty"`
HTTPPassword string `json:"httpPassword,omitempty"` HTTPPassword string `json:"httpPassword,omitempty"`
} }
func (o *HTTPProxyPluginOptions) Complete() {}
type HTTPS2HTTPPluginOptions struct { type HTTPS2HTTPPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"` LocalAddr string `json:"localAddr,omitempty"`
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"` HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"` RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
EnableHTTP2 *bool `json:"enableHTTP2,omitempty"`
CrtPath string `json:"crtPath,omitempty"` CrtPath string `json:"crtPath,omitempty"`
KeyPath string `json:"keyPath,omitempty"` KeyPath string `json:"keyPath,omitempty"`
} }
func (o *HTTPS2HTTPPluginOptions) Complete() {
o.EnableHTTP2 = util.EmptyOr(o.EnableHTTP2, lo.ToPtr(true))
}
type HTTPS2HTTPSPluginOptions struct { type HTTPS2HTTPSPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"` LocalAddr string `json:"localAddr,omitempty"`
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"` HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"` RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
EnableHTTP2 *bool `json:"enableHTTP2,omitempty"`
CrtPath string `json:"crtPath,omitempty"` CrtPath string `json:"crtPath,omitempty"`
KeyPath string `json:"keyPath,omitempty"` KeyPath string `json:"keyPath,omitempty"`
} }
func (o *HTTPS2HTTPSPluginOptions) Complete() {
o.EnableHTTP2 = util.EmptyOr(o.EnableHTTP2, lo.ToPtr(true))
}
type HTTP2HTTPPluginOptions struct {
Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"`
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
}
func (o *HTTP2HTTPPluginOptions) Complete() {}
type Socks5PluginOptions struct { type Socks5PluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
} }
func (o *Socks5PluginOptions) Complete() {}
type StaticFilePluginOptions struct { type StaticFilePluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
LocalPath string `json:"localPath,omitempty"` LocalPath string `json:"localPath,omitempty"`
@@ -166,11 +135,15 @@ type StaticFilePluginOptions struct {
HTTPPassword string `json:"httpPassword,omitempty"` HTTPPassword string `json:"httpPassword,omitempty"`
} }
func (o *StaticFilePluginOptions) Complete() {}
type UnixDomainSocketPluginOptions struct { type UnixDomainSocketPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
UnixPath string `json:"unixPath,omitempty"` UnixPath string `json:"unixPath,omitempty"`
} }
func (o *UnixDomainSocketPluginOptions) Complete() {} // Added HTTP2HTTPPluginOptions struct
type HTTP2HTTPPluginOptions struct {
Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"`
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
}

View File

@@ -127,10 +127,6 @@ func (c *ProxyBaseConfig) Complete(namePrefix string) {
c.Name = lo.Ternary(namePrefix == "", "", namePrefix+".") + c.Name c.Name = lo.Ternary(namePrefix == "", "", namePrefix+".") + c.Name
c.LocalIP = util.EmptyOr(c.LocalIP, "127.0.0.1") c.LocalIP = util.EmptyOr(c.LocalIP, "127.0.0.1")
c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient) c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient)
if c.Plugin.ClientPluginOptions != nil {
c.Plugin.ClientPluginOptions.Complete()
}
} }
func (c *ProxyBaseConfig) MarshalToMsg(m *msg.NewProxy) { func (c *ProxyBaseConfig) MarshalToMsg(m *msg.NewProxy) {

View File

@@ -27,11 +27,9 @@ import (
"time" "time"
"github.com/fatedier/golib/pool" "github.com/fatedier/golib/pool"
"github.com/samber/lo"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/transport"
httppkg "github.com/fatedier/frp/pkg/util/http"
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
netpkg "github.com/fatedier/frp/pkg/util/net" netpkg "github.com/fatedier/frp/pkg/util/net"
) )
@@ -73,17 +71,6 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
BufferPool: pool.NewBuffer(32 * 1024), BufferPool: pool.NewBuffer(32 * 1024),
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0), ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
} }
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.TLS != nil {
tlsServerName, _ := httppkg.CanonicalHost(r.TLS.ServerName)
host, _ := httppkg.CanonicalHost(r.Host)
if tlsServerName != "" && tlsServerName != host {
w.WriteHeader(http.StatusMisdirectedRequest)
return
}
}
rp.ServeHTTP(w, r)
})
var ( var (
tlsConfig *tls.Config tlsConfig *tls.Config
@@ -100,13 +87,10 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
} }
p.s = &http.Server{ p.s = &http.Server{
Handler: handler, Handler: rp,
ReadHeaderTimeout: 60 * time.Second, ReadHeaderTimeout: 60 * time.Second,
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
} }
if !lo.FromPtr(opts.EnableHTTP2) {
p.s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
}
go func() { go func() {
_ = p.s.ServeTLS(listener, "", "") _ = p.s.ServeTLS(listener, "", "")

View File

@@ -27,11 +27,9 @@ import (
"time" "time"
"github.com/fatedier/golib/pool" "github.com/fatedier/golib/pool"
"github.com/samber/lo"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/transport"
httppkg "github.com/fatedier/frp/pkg/util/http"
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
netpkg "github.com/fatedier/frp/pkg/util/net" netpkg "github.com/fatedier/frp/pkg/util/net"
) )
@@ -79,17 +77,6 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
BufferPool: pool.NewBuffer(32 * 1024), BufferPool: pool.NewBuffer(32 * 1024),
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0), ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
} }
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.TLS != nil {
tlsServerName, _ := httppkg.CanonicalHost(r.TLS.ServerName)
host, _ := httppkg.CanonicalHost(r.Host)
if tlsServerName != "" && tlsServerName != host {
w.WriteHeader(http.StatusMisdirectedRequest)
return
}
}
rp.ServeHTTP(w, r)
})
var ( var (
tlsConfig *tls.Config tlsConfig *tls.Config
@@ -106,13 +93,10 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
} }
p.s = &http.Server{ p.s = &http.Server{
Handler: handler, Handler: rp,
ReadHeaderTimeout: 60 * time.Second, ReadHeaderTimeout: 60 * time.Second,
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
} }
if !lo.FromPtr(opts.EnableHTTP2) {
p.s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
}
go func() { go func() {
_ = p.s.ServeTLS(listener, "", "") _ = p.s.ServeTLS(listener, "", "")

View File

@@ -14,7 +14,7 @@
package version package version
var version = "0.59.0" var version = "0.58.1"
func Full() string { func Full() string {
return version return version