mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
plugin: add http_proxy
This commit is contained in:
parent
e6d82f3162
commit
03d55201b2
@ -2,7 +2,6 @@ sudo: false
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
- 1.8.x
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
@ -103,7 +103,6 @@ func (pxy *TcpProxy) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
defer conn.Close()
|
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +131,6 @@ func (pxy *HttpProxy) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
defer conn.Close()
|
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +159,6 @@ func (pxy *HttpsProxy) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
defer conn.Close()
|
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,11 +90,18 @@ use_compression = false
|
|||||||
subdomain = web01
|
subdomain = web01
|
||||||
custom_domains = web02.yourdomain.com
|
custom_domains = web02.yourdomain.com
|
||||||
|
|
||||||
[unix_domain_socket]
|
[plugin_unix_domain_socket]
|
||||||
type = tcp
|
type = tcp
|
||||||
remote_port = 6001
|
remote_port = 6003
|
||||||
# if plugin is defined, local_ip and local_port is useless
|
# if plugin is defined, local_ip and local_port is useless
|
||||||
# plugin will handle connections got from frps
|
# plugin will handle connections got from frps
|
||||||
plugin = unix_domain_socket
|
plugin = unix_domain_socket
|
||||||
# params set with prefix "plugin_" that plugin needed
|
# params set with prefix "plugin_" that plugin needed
|
||||||
plugin_unix_path = /var/run/docker.sock
|
plugin_unix_path = /var/run/docker.sock
|
||||||
|
|
||||||
|
[plugin_http_proxy]
|
||||||
|
type = tcp
|
||||||
|
remote_port = 6004
|
||||||
|
plugin = http_proxy
|
||||||
|
plugin_http_user = abc
|
||||||
|
plugin_http_passwd = abc
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"port":":8080"
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config 保存代理服务器的配置
|
|
||||||
type Config struct {
|
|
||||||
Port string `json:"port"`
|
|
||||||
Auth bool `json:"auth"`
|
|
||||||
|
|
||||||
User map[string]string `json:"user"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从指定json文件读取config配置
|
|
||||||
func (c *Config) GetConfig(filename string) error {
|
|
||||||
|
|
||||||
configFile, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer configFile.Close()
|
|
||||||
|
|
||||||
br := bufio.NewReader(configFile)
|
|
||||||
err = json.NewDecoder(br).Decode(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,203 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/plugin"
|
|
||||||
"github.com/fatedier/frp/utils/log"
|
|
||||||
wrap "github.com/fatedier/frp/utils/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
HTTP_200 = []byte("HTTP/1.1 200 Connection Established\r\n\r\n")
|
|
||||||
ProxyName = "default-proxy"
|
|
||||||
cnfg Config
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// 加载配置文件
|
|
||||||
err := cnfg.GetConfig("../config/config.json")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("can not load config file:%v\n", err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
plugin.Register(ProxyName, NewProxyPlugin)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyServer struct {
|
|
||||||
Tr *http.Transport
|
|
||||||
}
|
|
||||||
|
|
||||||
type Proxy struct {
|
|
||||||
Server *http.Server
|
|
||||||
Ln net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProxyPlugin(params map[string]string) (p plugin.Plugin, err error) {
|
|
||||||
|
|
||||||
listen, err := net.Listen("tcp", cnfg.Port)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("can not listen %v port", cnfg.Port)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy := &Proxy{
|
|
||||||
Server: NewProxyServer(),
|
|
||||||
Ln: listen,
|
|
||||||
}
|
|
||||||
go proxy.Server.Serve(proxy.Ln)
|
|
||||||
|
|
||||||
return proxy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proxy *Proxy) Name() string {
|
|
||||||
return ProxyName
|
|
||||||
}
|
|
||||||
|
|
||||||
// right??
|
|
||||||
func (proxy *Proxy) Handle(conn io.ReadWriteCloser) {
|
|
||||||
wrapConn := wrap.WrapReadWriteCloserToConn(conn)
|
|
||||||
|
|
||||||
remote, err := net.Dial("tcp", cnfg.Port)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("dial tcp error:%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// or tcp.Join(remote,wrapConn)
|
|
||||||
_, err = io.Copy(remote, wrapConn)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.Error("io copy data error:%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proxy *Proxy) Close() error {
|
|
||||||
return proxy.Server.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProxyServer() *http.Server {
|
|
||||||
|
|
||||||
return &http.Server{
|
|
||||||
Addr: cnfg.Port,
|
|
||||||
Handler: &ProxyServer{Tr: http.DefaultTransport.(*http.Transport)},
|
|
||||||
ReadTimeout: 10 * time.Second,
|
|
||||||
WriteTimeout: 10 * time.Second,
|
|
||||||
MaxHeaderBytes: 1 << 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proxy *ProxyServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
|
||||||
log.Error("Panic: %v", err)
|
|
||||||
fmt.Fprintf(rw, fmt.Sprintln(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if req.Method == "CONNECT" { // 是connect连接
|
|
||||||
proxy.HttpsHandler(rw, req)
|
|
||||||
} else {
|
|
||||||
proxy.HttpHandler(rw, req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理普通的http请求
|
|
||||||
func (proxy *ProxyServer) HttpHandler(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
log.Info("is sending request %v %v ", req.Method, req.URL.Host)
|
|
||||||
removeProxyHeaders(req) // 去除不必要的头
|
|
||||||
|
|
||||||
resp, err := proxy.Tr.RoundTrip(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("transport RoundTrip error: %v", err)
|
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
clearHeaders(rw.Header()) // 得到一个空的Header
|
|
||||||
copyHeaders(rw.Header(), resp.Header)
|
|
||||||
rw.WriteHeader(resp.StatusCode)
|
|
||||||
|
|
||||||
nr, err := io.Copy(rw, resp.Body)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.Error("got an error when copy remote response to client.%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("copied %v bytes from remote host %v.", nr, req.URL.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理https连接,主要用于CONNECT方法
|
|
||||||
func (proxy *ProxyServer) HttpsHandler(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
log.Info("[CONNECT] tried to connect to remote host %v", req.URL.Host)
|
|
||||||
|
|
||||||
hj, _ := rw.(http.Hijacker)
|
|
||||||
client, _, err := hj.Hijack() //获取客户端与代理服务器的tcp连接
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to get Tcp connection of", req.RequestURI)
|
|
||||||
http.Error(rw, "Failed", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
remote, err := net.Dial("tcp", req.URL.Host) //建立服务端和代理服务器的tcp连接
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to connect %v", req.RequestURI)
|
|
||||||
http.Error(rw, "Failed", http.StatusBadRequest)
|
|
||||||
client.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Write(HTTP_200)
|
|
||||||
|
|
||||||
go copyRemoteToClient(remote, client)
|
|
||||||
go copyRemoteToClient(client, remote)
|
|
||||||
}
|
|
||||||
|
|
||||||
// data copy between two socket
|
|
||||||
func copyRemoteToClient(remote, client net.Conn) {
|
|
||||||
defer func() {
|
|
||||||
remote.Close()
|
|
||||||
client.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
nr, err := io.Copy(remote, client)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.Error("got an error when handles CONNECT %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("[CONNECT] transported %v bytes betwwen %v and %v", nr, remote.RemoteAddr(), client.RemoteAddr())
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyHeaders(dst, src http.Header) {
|
|
||||||
for key, values := range src {
|
|
||||||
for _, value := range values {
|
|
||||||
dst.Add(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func clearHeaders(headers http.Header) {
|
|
||||||
for key, _ := range headers {
|
|
||||||
headers.Del(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeProxyHeaders(req *http.Request) {
|
|
||||||
req.RequestURI = ""
|
|
||||||
req.Header.Del("Proxy-Connection")
|
|
||||||
req.Header.Del("Connection")
|
|
||||||
req.Header.Del("Keep-Alive")
|
|
||||||
req.Header.Del("Proxy-Authenticate")
|
|
||||||
req.Header.Del("Proxy-Authorization")
|
|
||||||
req.Header.Del("TE")
|
|
||||||
req.Header.Del("Trailers")
|
|
||||||
req.Header.Del("Transfer-Encoding")
|
|
||||||
req.Header.Del("Upgrade")
|
|
||||||
}
|
|
226
models/plugin/http_proxy.go
Normal file
226
models/plugin/http_proxy.go
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
// Copyright 2017 frp team
|
||||||
|
//
|
||||||
|
// 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 plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/proto/tcp"
|
||||||
|
"github.com/fatedier/frp/utils/errors"
|
||||||
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PluginHttpProxy = "http_proxy"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(PluginHttpProxy, NewHttpProxyPlugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
conns chan net.Conn
|
||||||
|
closed bool
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyListener() *Listener {
|
||||||
|
return &Listener{
|
||||||
|
conns: make(chan net.Conn, 64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Accept() (net.Conn, error) {
|
||||||
|
conn, ok := <-l.conns
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("listener closed")
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) PutConn(conn net.Conn) error {
|
||||||
|
err := errors.PanicToError(func() {
|
||||||
|
l.conns <- conn
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
if !l.closed {
|
||||||
|
close(l.conns)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Addr() net.Addr {
|
||||||
|
return (*net.TCPAddr)(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HttpProxy struct {
|
||||||
|
l *Listener
|
||||||
|
s *http.Server
|
||||||
|
AuthUser string
|
||||||
|
AuthPasswd string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHttpProxyPlugin(params map[string]string) (Plugin, error) {
|
||||||
|
user := params["plugin_http_user"]
|
||||||
|
passwd := params["plugin_http_passwd"]
|
||||||
|
listener := NewProxyListener()
|
||||||
|
|
||||||
|
hp := &HttpProxy{
|
||||||
|
l: listener,
|
||||||
|
AuthUser: user,
|
||||||
|
AuthPasswd: passwd,
|
||||||
|
}
|
||||||
|
|
||||||
|
hp.s = &http.Server{
|
||||||
|
Handler: hp,
|
||||||
|
}
|
||||||
|
|
||||||
|
go hp.s.Serve(listener)
|
||||||
|
return hp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HttpProxy) Name() string {
|
||||||
|
return PluginHttpProxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HttpProxy) Handle(conn io.ReadWriteCloser) {
|
||||||
|
var wrapConn net.Conn
|
||||||
|
if realConn, ok := conn.(net.Conn); ok {
|
||||||
|
wrapConn = realConn
|
||||||
|
} else {
|
||||||
|
wrapConn = frpNet.WrapReadWriteCloserToConn(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
hp.l.PutConn(wrapConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HttpProxy) Close() error {
|
||||||
|
hp.s.Close()
|
||||||
|
hp.l.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HttpProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if ok := hp.Auth(rw, req); !ok {
|
||||||
|
rw.Header().Set("Proxy-Authenticate", "Basic")
|
||||||
|
rw.WriteHeader(http.StatusProxyAuthRequired)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method == "CONNECT" {
|
||||||
|
hp.ConnectHandler(rw, req)
|
||||||
|
} else {
|
||||||
|
hp.HttpHandler(rw, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HttpProxy) HttpHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
removeProxyHeaders(req)
|
||||||
|
|
||||||
|
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
copyHeaders(rw.Header(), resp.Header)
|
||||||
|
rw.WriteHeader(resp.StatusCode)
|
||||||
|
|
||||||
|
_, err = io.Copy(rw, resp.Body)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HttpProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
hj, ok := rw.(http.Hijacker)
|
||||||
|
if !ok {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client, _, err := hj.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
remote, err := net.Dial("tcp", req.URL.Host)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, "Failed", http.StatusBadRequest)
|
||||||
|
client.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
|
||||||
|
|
||||||
|
go tcp.Join(remote, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HttpProxy) Auth(rw http.ResponseWriter, req *http.Request) bool {
|
||||||
|
if hp.AuthUser == "" && hp.AuthPasswd == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
s := strings.SplitN(req.Header.Get("Proxy-Authorization"), " ", 2)
|
||||||
|
if len(s) != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pair := strings.SplitN(string(b), ":", 2)
|
||||||
|
if len(pair) != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if pair[0] != hp.AuthUser || pair[1] != hp.AuthPasswd {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyHeaders(dst, src http.Header) {
|
||||||
|
for key, values := range src {
|
||||||
|
for _, value := range values {
|
||||||
|
dst.Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeProxyHeaders(req *http.Request) {
|
||||||
|
req.RequestURI = ""
|
||||||
|
req.Header.Del("Proxy-Connection")
|
||||||
|
req.Header.Del("Connection")
|
||||||
|
req.Header.Del("Proxy-Authenticate")
|
||||||
|
req.Header.Del("Proxy-Authorization")
|
||||||
|
req.Header.Del("TE")
|
||||||
|
req.Header.Del("Trailers")
|
||||||
|
req.Header.Del("Transfer-Encoding")
|
||||||
|
req.Header.Del("Upgrade")
|
||||||
|
}
|
@ -46,11 +46,11 @@ type WrapReadWriteCloserConn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (conn *WrapReadWriteCloserConn) LocalAddr() net.Addr {
|
func (conn *WrapReadWriteCloserConn) LocalAddr() net.Addr {
|
||||||
return nil
|
return (*net.TCPAddr)(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *WrapReadWriteCloserConn) RemoteAddr() net.Addr {
|
func (conn *WrapReadWriteCloserConn) RemoteAddr() net.Addr {
|
||||||
return nil
|
return (*net.TCPAddr)(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *WrapReadWriteCloserConn) SetDeadline(t time.Time) error {
|
func (conn *WrapReadWriteCloserConn) SetDeadline(t time.Time) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user