Repository: fatedier/frp (language: go)

Issue:
'localAddr' of https2http seems not working.
### Bug Description

When using `frpc` and `frps` version 0.58.0, using `https2http` plugin to proxy LAN HTTP service to Internet, frpc seems not working.

The output log shows it dials TCP `127.0.0.1:0` instead of `127.0.0.1:3000`, which is abnormal.

I checked `netstat -lntp`, shows the following output:

```
[root@abc frpc]# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1248/sshd: /usr/sbi 
tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN      253334/docker-proxy 
tcp        0      0 0.0.0.0:8989            0.0.0.0:*               LISTEN      2312/docker-proxy   
tcp        0      0 0.0.0.0:2222            0.0.0.0:*               LISTEN      253353/docker-proxy 
tcp        0      0 0.0.0.0:5230            0.0.0.0:*               LISTEN      2294/docker-proxy   
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      1011/rpcbind        
tcp        0      0 0.0.0.0:2224            0.0.0.0:*               LISTEN      2334/docker-proxy   
tcp        0      0 0.0.0.0:8081            0.0.0.0:*               LISTEN      2272/docker-proxy   
tcp6       0      0 :::22                   :::*                    LISTEN      1248/sshd: /usr/sbi 
tcp6       0      0 :::3000                 :::*                    LISTEN      253341/docker-proxy 
tcp6       0      0 :::8989                 :::*                    LISTEN      2321/docker-proxy   
tcp6       0      0 :::2222                 :::*                    LISTEN      253360/docker-proxy 
tcp6       0      0 :::5230                 :::*                    LISTEN      2304/docker-proxy   
tcp6       0      0 :::111                  :::*                    LISTEN      1011/rpcbind        
tcp6       0      0 :::2224                 :::*                    LISTEN      2340/docker-proxy   
tcp6       0      0 :::8081                 :::*                    LISTEN      2279/docker-proxy
```

Which indicates the TCP port 3000 is open.

### frpc Version

0.58.0

### frps Version

0.58.0

### System Architecture

linux/arm64

### Configurations

frps.toml
```toml
bindAddr = "0.0.0.0"
bindPort = 7000

vhostHTTPPort = 8080
vhostHTTPSPort = 8443

subDomainHost = "domain.ext"

auth.method = "token"
auth.token = "12345678"
```

frpc.toml
```toml
user = "abc"

serverAddr = "xxx.xxx.xxx.xxx"
serverPort = 7000

auth.method = "token"
auth.token = "12345678"


[[proxies]]
name = "gitea-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 2222
remotePort = 2222


[[proxies]]
name = "gitea-https"
type = "https"
subdomain = "git"
[proxies.plugin]
name = "https2http"
localAddr = "127.0.0.1:3000"
crtPath = "domain.pem"
keyPath = "domain.key"
hostHeaderRewrite = "127.0.0.1"
requestHeaders.set.x-from-where = "frp"
```

### Logs

```
2024-05-23 12:11:35.690 [I] [sub/root.go:142] start frpc service for config file [frpc.toml]
2024-05-23 12:11:35.690 [I] [client/service.go:294] try to connect to server...
2024-05-23 12:11:35.774 [I] [client/service.go:286] [df40e7da7806f48e] login to server success, get run id [df40e7da7806f48e]
2024-05-23 12:11:35.774 [I] [proxy/proxy_manager.go:173] [df40e7da7806f48e] proxy added: [abc.gitea-ssh abc.gitea-https]
2024-05-23 12:11:35.805 [I] [client/control.go:168] [df40e7da7806f48e] [abc.gitea-https] start proxy success
2024-05-23 12:11:35.806 [I] [client/control.go:168] [df40e7da7806f48e] [abc.gitea-ssh] start proxy success
2024-05-23 12:11:49.541 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
2024-05-23 12:11:49.665 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
2024-05-23 12:11:50.527 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
2024-05-23 12:11:50.655 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
2024-05-23 12:11:56.531 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
2024-05-23 12:11:57.524 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
2024-05-23 12:11:58.246 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
2024-05-23 12:11:59.229 [E] [proxy/proxy.go:206] [df40e7da7806f48e] [abc.gitea-https] connect to local service [127.0.0.1:0] error: dial tcp 127.0.0.1:0: connect: connection refused
```

### Steps to reproduce

1. Set `*.domain.ext` A record to IP Address `xxx.xxx.xxx.xxx`.
2. Use above configuration file and version 0.58.0 arm64 binaries.
3. Start `frps -c frps.toml`
4. Start `frpc -c frpc.toml`
5. Use browser to navigate to `https://git.domain.ext:8443` 
...


### Affected area

- [ ] Docs
- [ ] Installation
- [ ] Performance and Scalability
- [ ] Security
- [X] User Experience
- [ ] Test and Release
- [ ] Developer Infrastructure
- [X] Client Plugin
- [ ] Server Plugin
- [ ] Extensions
- [ ] Others


Discussion / hints:

Fix patch:
diff --git a/pkg/config/v1/plugin.go b/pkg/config/v1/plugin.go
index 5602a813c3c..3a7c8344a8b 100644
--- a/pkg/config/v1/plugin.go
+++ b/pkg/config/v1/plugin.go
@@ -17,6 +17,7 @@ package v1
 import (
 	"bytes"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"reflect"
 )
@@ -42,7 +43,7 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
 
 	c.Type = typeStruct.Type
 	if c.Type == "" {
-		return nil
+		return errors.New("plugin type is empty")
 	}
 
 	v, ok := clientPluginOptionsTypeMap[typeStruct.Type]
@@ -63,6 +64,10 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
 	return nil
 }
 
+func (c *TypedClientPluginOptions) MarshalJSON() ([]byte, error) {
+	return json.Marshal(c.ClientPluginOptions)
+}
+
 const (
 	PluginHTTP2HTTPS       = "http2https"
 	PluginHTTPProxy        = "http_proxy"
diff --git a/pkg/config/v1/proxy.go b/pkg/config/v1/proxy.go
index 8530e2162f7..45c489f689c 100644
--- a/pkg/config/v1/proxy.go
+++ b/pkg/config/v1/proxy.go
@@ -195,6 +195,10 @@ func (c *TypedProxyConfig) UnmarshalJSON(b []byte) error {
 	return nil
 }
 
+func (c *TypedProxyConfig) MarshalJSON() ([]byte, error) {
+	return json.Marshal(c.ProxyConfigurer)
+}
+
 type ProxyConfigurer interface {
 	Complete(namePrefix string)
 	GetBaseConfig() *ProxyBaseConfig
diff --git a/pkg/config/v1/visitor.go b/pkg/config/v1/visitor.go
index e9fa166ea57..51fe88a6300 100644
--- a/pkg/config/v1/visitor.go
+++ b/pkg/config/v1/visitor.go
@@ -120,6 +120,10 @@ func (c *TypedVisitorConfig) UnmarshalJSON(b []byte) error {
 	return nil
 }
 
+func (c *TypedVisitorConfig) MarshalJSON() ([]byte, error) {
+	return json.Marshal(c.VisitorConfigurer)
+}
+
 func NewVisitorConfigurerByType(t VisitorType) VisitorConfigurer {
 	v, ok := visitorConfigTypeMap[t]
 	if !ok {


Test patch: