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

Compatibility with Gorilla Websockets #25

Open
michaelweber opened this issue Apr 10, 2024 · 0 comments
Open

Compatibility with Gorilla Websockets #25

michaelweber opened this issue Apr 10, 2024 · 0 comments

Comments

@michaelweber
Copy link

Hi!

I've been trying to compile some of the example netcode in the gorilla/websockets project using the net extensions package. Specifically I'm going with the fairly straightforward echo server example at https://github.com/gorilla/websocket/tree/main/examples/echo.

I have no issues when I try to replace the server code (in server.go) with the wasip1 bindings, I just swap a single err := server.ListenAndServe() line out and replace it with a call to wasip1.Listen along with a follow-up definition of an http.Server and call to .Serve(). I can use wasirun to execute the example perfectly by building with GOOS=wasip1 GOARCH=wasm go build -o server.wasm server.go and then running wasirun server.wasm. It works perfectly!

When I try to replace the client code however, I run into some weirdness. I'm including the entire file of client.go here after my changes:

// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore
// +build ignore

package main

import (
	"flag"
	"github.com/stealthrocket/net/wasip1"
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/websocket"
)

var addr = flag.String("addr", "127.0.0.1:8080", "http service address")

func main() {
	flag.Parse()
	log.SetFlags(0)

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
	log.Printf("connecting to %s", u.String())

	dialer := websocket.DefaultDialer
	dialer.NetDialContext = wasip1.DialContext
	dialer.NetDialTLSContext = wasip1.DialContext

	c, _, err := dialer.Dial(u.String(), nil)

	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer close(done)
		for {
			mt, message, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
			log.Printf("recv: %s, type: %s", message, websocket.FormatMessageType(mt))
		}
	}()

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-done:
			return
		case t := <-ticker.C:
			err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
			if err != nil {
				log.Println("write:", err)
				return
			}
		case <-interrupt:
			log.Println("interrupt")

			// Cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			return
		}
	}
}

The key change I've tried to make here is that I've swapped:

c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)

into

dialer := websocket.DefaultDialer
dialer.NetDialContext = wasip1.DialContext
dialer.NetDialTLSContext = wasip1.DialContext
c, _, err := dialer.Dial(u.String(), nil)

From what I can tell in the codebase at https://github.com/gorilla/websocket/blob/main/client.go#L255-L277 - this should be the right place to be replacing the dial contexts, though I'm not certain.

I compile the code with GOOS=wasip1 GOARCH=wasm go build -o client.wasm client.go and then try running it with wasirun --trace --dial 127.0.0.1:8080 --sockets=auto --non-blocking-stdio client.wasm. It hangs for a while and then the connection times out. What's interesting is that it only hangs if the server is listening, otherwise I get an immediate dial:dial tcp 127.0.0.1:8080: connect: Connection refused error. And after it times out (dial:read tcp 127.0.0.1:60849->127.0.0.1:8080: i/o timeout) I can see the server warn me that a connection to it has been reset, read: read tcp 127.0.0.1:8080->127.0.0.1:60849: fd_read: Connection reset by peer. So it looks like this is at least partially working, but I'm not quite sure why it's hanging and timing out. Maybe there's an issue with threading I need to figure out how to avoid?

Any tips on how to proceed further / am I using the package incorrectly?

Cheers!

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

No branches or pull requests

1 participant