func wsCopyResponse(dst *fasthttp.Response, src *http.Response) error {
	for k, vv := range src.Header {
		for _, v := range vv {
			dst.Header.Add(k, v)
		}
	}
	dst.SetStatusCode(src.StatusCode)
	defer src.Body.Close()
	buf := bytes.NewBuffer(nil)
	if _, err := io.Copy(buf, src.Body); err != nil {
		return err
	}
	dst.SetBody(buf.Bytes())
	return nil
}
wsCopyResponse .
to help copy origin websocket response to client
package proxy
import (
	"bytes"
	"fmt"
	"io"
	"net"
	"net/http"
	"strings"
	"github.com/yeqown/log"
	"github.com/fasthttp/websocket"
	"github.com/valyala/fasthttp"
)
var (
	DefaultUpgrader = &websocket.FastHTTPUpgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}
	DefaultDialer = websocket.DefaultDialer
)
type WSReverseProxy struct {
	option *buildOptionWS
}
func NewWSReverseProxy(host, path string) *WSReverseProxy {
	path = strings.TrimPrefix(path, "/")
	wsproxy, err := NewWSReverseProxyWith(
		WithURL_OptionWS(fmt.Sprintf("%s://%s/%s", "ws", host, path)),
	)
	if err != nil {
		panic(err)
	}
	return wsproxy
}
func NewWSReverseProxyWith(opts ...OptionWS) (*WSReverseProxy, error) {
	dst := defaultBuildOptionWS()
	for _, opt := range opts {
		opt.apply(dst)
	}
	if err := dst.validate(); err != nil {
		return nil, err
	}
	return &WSReverseProxy{
		option: dst,
	}, nil
}
func (w *WSReverseProxy) ServeHTTP(ctx *fasthttp.RequestCtx) {
	if b := websocket.FastHTTPIsWebSocketUpgrade(ctx); b {
		logger.Debugf("Request is upgraded %v", b)
	}
	var (
		resp     = &ctx.Response
		dialer   = DefaultDialer
		upgrader = DefaultUpgrader
	)
	if w.option.dialer != nil {
		dialer = w.option.dialer
	}
	if w.option.upgrader != nil {
		upgrader = w.option.upgrader
	}
	forwardHeader := builtinForwardHeaderHandler(ctx)
	if w.option.fn != nil {
		appendHeaders := w.option.fn(ctx)
		for k, vs := range appendHeaders {
			for _, v := range vs {
				forwardHeader.Set(k, v)
			}
		}
	}
	connBackend, respBackend, err := dialer.Dial(w.option.target.String(), forwardHeader)
	if err != nil {
		logger.
			WithFields(log.Fields{
				"error": err,
				"host":  w.option.target.String(),
			}).
			Errorf("websocketproxy: couldn't dial to remote backend")
		if respBackend != nil {
			if err = wsCopyResponse(resp, respBackend); err != nil {
				logger.Errorf("could not finish wsCopyResponse, err=%v", err)
			}
		} else {
			ctx.Error(err.Error(), fasthttp.StatusServiceUnavailable)
		}
		return
	}
	err = upgrader.Upgrade(ctx, func(connPub *websocket.Conn) {
		defer connPub.Close()
		var (
			errClient  = make(chan error, 1)
			errBackend = make(chan error, 1)
			message    string
		)
		logger.Debug("upgrade handler working")
		go replicateWebsocketConn(connPub, connBackend, errClient)   
		go replicateWebsocketConn(connBackend, connPub, errBackend)  
		for {
			select {
			case err = <-errClient:
				message = "websocketproxy: Error when copying response: %v"
			case err = <-errBackend:
				message = "websocketproxy: Error when copying request: %v"
			}
			if _, ok := err.(*websocket.CloseError); !ok {
				logger.Errorf(message, err)
			}
		}
	})
	if err != nil {
		logger.Errorf("websocketproxy: couldn't upgrade %s", err)
		return
	}
}
func builtinForwardHeaderHandler(ctx *fasthttp.RequestCtx) (forwardHeader http.Header) {
	forwardHeader = make(http.Header, 4)
	if origin := ctx.Request.Header.Peek("Origin"); string(origin) != "" {
		forwardHeader.Add("Origin", string(origin))
	}
	if prot := ctx.Request.Header.Peek("Sec-WebSocket-Protocol"); string(prot) != "" {
		forwardHeader.Add("Sec-WebSocket-Protocol", string(prot))
	}
	if cookie := ctx.Request.Header.Peek("Cookie"); string(cookie) != "" {
		forwardHeader.Add("Sec-WebSocket-Protocol", string(cookie))
	}
	if string(ctx.Request.Host()) != "" {
		forwardHeader.Set("Host", string(ctx.Request.Host()))
	}
	if clientIP, _, err := net.SplitHostPort(ctx.RemoteAddr().String()); err == nil {
		if prior := ctx.Request.Header.Peek("X-Forwarded-For"); string(prior) != "" {
			clientIP = string(prior) + ", " + clientIP
		}
		forwardHeader.Set("X-Forwarded-For", clientIP)
	}
	forwardHeader.Set("X-Forwarded-Proto", "http")
	if ctx.IsTLS() {
		forwardHeader.Set("X-Forwarded-Proto", "https")
	}
	return
}
func replicateWebsocketConn(dst, src *websocket.Conn, errChan chan error) {
	for {
		msgType, msg, err := src.ReadMessage()
		if err != nil {
			logger.Debugf("src.ReadMessage failed, msgType=%d, msg=%s, err=%v", msgType, msg, err)
			if ce, ok := err.(*websocket.CloseError); ok {
				msg = websocket.FormatCloseMessage(ce.Code, ce.Text)
			} else {
				logger.Errorf("src.ReadMessage failed, err=%v", err)
				msg = websocket.FormatCloseMessage(websocket.CloseAbnormalClosure, err.Error())
			}
			errChan <- err
			if err = dst.WriteMessage(websocket.CloseMessage, msg); err != nil {
				logger.Errorf("write close message failed, err=%v", err)
			}
			break
		}
		err = dst.WriteMessage(msgType, msg)
		if err != nil {
			logger.Errorf("dst.WriteMessage failed, err=%v", err)
			errChan <- err
			break
		}
	}
}