Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🤗 [Question]: How to proxy for websocket using go fiber ? #2161

Open
3 tasks done
varunkrishnaibm opened this issue Oct 18, 2022 · 9 comments
Open
3 tasks done

🤗 [Question]: How to proxy for websocket using go fiber ? #2161

varunkrishnaibm opened this issue Oct 18, 2022 · 9 comments

Comments

@varunkrishnaibm
Copy link

Question Description

Hi, I am new to go lang and I am using go fiber to do the proxy to the backend server. All other api calls working fine with proxy.do but for the websocket it keeps failing. Any help is appreciated. Feel free to point out the mistake if you find any. Thanks.

Additional info is I am seeing the 101 status but after that I see that on the client side it says bad hand shake.

I also checked the prior issue which is similar but none of them points to solution.

Code Snippet (optional)

app.Use("/servicename/wsFeature", func(c *fiber.Ctx) error {
        
microserviceEndpoint := "forward url will be added here"
logger.Log.Info("Inside the Websocket api block")
        
if proxyErr := proxyReq(c, microserviceEndpoint);   proxyErr != nil {
      logger.Log.Error(proxyErr)
    return proxyErr
}
return nil
}
return errorHandler(c)
    })


func proxyReq(c *fiber.Ctx, microserviceEndpoint string) error {
    modifiedEndPoint := strings.Replace(c.OriginalURL(), "/servicename", "", 1)
    finalURL := microserviceEndpoint + modifiedEndPoint
    if err := proxy.Do(c, finalURL); err != nil {
        return err
    }
    logger.Log.Info(c.Response().StatusCode())
    return nil
}



location /servicename/wsfeature {
            auth_request        /auth;
            include    /usr/local/openresty/nginx/conf/proxy.conf;
            rewrite ^/servicename(.*)$ $1 break;
            proxy_pass forwardURL:port;
        }


The above nginx is working but migrating to the go fiber fails.

Checklist:

  • I agree to follow Fiber's Code of Conduct.
  • I have checked for existing issues that describe my questions prior to opening this one.
  • I understand that improperly formatted questions may be closed without explanation.
@welcome
Copy link

welcome bot commented Oct 18, 2022

Thanks for opening your first issue here! 🎉 Be sure to follow the issue template! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

@li-jin-gou
Copy link
Contributor

refer to #1976

@varunkrishnaibm
Copy link
Author

@li-jin-gou I had gone through that ticket. But my issue is similar to this ticket #1942 (comment). The ticket author found out a workaround in the end but now the one of the package is not available. Also I cannot use the packages which is not from trusted sources or not actively supported. Hence I was looking for different solution.

@li-jin-gou
Copy link
Contributor

I think maybe we can support the websocket proxy and what do you think? @ReneWerner87 @efectn

@efectn
Copy link
Member

efectn commented Oct 19, 2022

I think maybe we can support the websocket proxy and what do you think? @ReneWerner87 @efectn

Agree

@ReneWerner87
Copy link
Member

yes sure, just am not a websocket specialist need support for this

@varunkrishnaibm
Copy link
Author

varunkrishnaibm commented Oct 20, 2022

Also along with Websocket we use Server side events, I know the go fiber does not support proxy to SSE also (if it supports let me know). But you guys can think of it.

We made a decision to migrate from nginx to go fiber for building a middleware and reverse proxy, the first package suggested was Go fiber. When I read documents I was only looking for proxy and I got it but after majority of development was done and all APIs working but we got stuck at Websocket and SSE. We did not know that the proxy to these is not possible.

Suggestion: If you can mention that the proxy to websocket and sse is not possible in the docs it would help others from getting started and getting stuck at the end. If already mentioned in your docs let me know.

Here is the nignx code snippet for SSE this is just for reference:

 location ~ ^.*\b(sse)\b.*$ {
            auth_request        /auth;
            proxy_http_version 1.1;   
            proxy_set_header Connection "";            
            proxy_set_header Host $host; 
            proxy_set_header id $id; 
            proxy_set_header name $name;      
            proxy_set_header authnId $authnId; 
            proxy_set_header authnName $authnName;
            proxy_read_timeout 3600s;
            proxy_buffering off;
            proxy_cache off;
            chunked_transfer_encoding off;    
            add_header 'Access-Control-Allow-Origin' '*';        
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,Cache-Control,Content-Type,origin, accept, authorization, Keep-Alive';        
            rewrite ^/portionOfURL(.*)$ $1 break;
            proxy_pass forwardURL:port;
            
        }

Thank you

@benz9527
Copy link

benz9527 commented Mar 2, 2023

  1. If you have an Nginx as upstream server, you have to use the HTTP1.1 at least, then set the proxy_header connection as 'Upgrade' instead of 'Close'. Check the nginx server official configuration about websocket: https://nginx.org/en/docs/http/websocket.html
  2. When fiber has used the fiber websocket middleware, if fiber in running accepted client websocket request and established the websocket connection, you could use gorilla wesocket lib to create a new request to do request to another server. Make the fiber server as a websocket client for others, when fiber fetch the response data from others then send the data to orignal client also.

@LittleSheep2Code
Copy link

I use fasthttp/websocket to proxy websocket. And use FastHTTPIsWebSocketUpgrade to get request need to be upgrade to websocket, so that I can handle normal request at the same time.

// Destination is my own config of remote server
// used to store some remote server info such as ip address etc

func makeHypertextResponse(c *fiber.Ctx, dest *Destination) error {
	if websocket.FastHTTPIsWebSocketUpgrade(c.Context()) {
		// Handle websocket
		return makeWebsocketResponse(c, dest)
	} else {
		// Handle normal request
		return proxy.Do(c, dest.MakeUri(c), &fasthttp.Client{
			ReadTimeout:  3000,
			WriteTimeout: 3000,
		})
	}
}

var wsUpgrader = websocket.FastHTTPUpgrader{}

func makeWebsocketResponse(c *fiber.Ctx, dest *Destination) error {
	uri := dest.MakeWebsocketUri(c) // Just get the remote url

	// Upgrade connection
	return wsUpgrader.Upgrade(c.Context(), func(conn *websocket.Conn) {
		// Dial the destination
		remote, _, err := websocket.DefaultDialer.Dial(uri, nil)
		if err != nil {
			return
		}
		defer remote.Close()

		// Read messages from remote
		disconnect := make(chan struct{})
		signal := make(chan struct {
			head int
			data []byte
		})
		go func() {
			defer close(disconnect)
			for {
				mode, message, err := remote.ReadMessage()
				if err != nil {
					fmt.Println(err)
					return
				} else {
					signal <- struct {
						head int
						data []byte
					}{head: mode, data: message}
				}
			}
		}()

		// Relay the destination websocket to client
		for {
			select {
			case <-disconnect:
			case val := <-signal:
				if err := conn.WriteMessage(val.head, val.data); err != nil {
					return
				}
			default:
				if head, data, err := conn.ReadMessage(); err != nil {
					return
				} else {
					remote.WriteMessage(head, data)
				}
			}
		}
	})
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants