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

Help with echo server #117

Closed
anderspitman opened this issue Mar 8, 2024 · 4 comments
Closed

Help with echo server #117

anderspitman opened this issue Mar 8, 2024 · 4 comments

Comments

@anderspitman
Copy link

anderspitman commented Mar 8, 2024

I think I have some fundamental misunderstandings about how to use this library. I'm trying to get a simple echo server running. The following is a self-contained example (uses certmagic to automatically get Let's Encrypt certs):

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net/http"

	"github.com/caddyserver/certmagic"
	"github.com/quic-go/quic-go"
	"github.com/quic-go/quic-go/http3"
	"github.com/quic-go/webtransport-go"
)

func main() {
	go runServer()
	runClient()
}

func runServer() {

	tlsConfig := getTlsConfig()

	ctx := context.Background()

	wtServer := webtransport.Server{
		H3: http3.Server{
			Addr:       ":443",
			TLSConfig:  tlsConfig,
			QuicConfig: &quic.Config{},
		},
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		wtSession, err := wtServer.Upgrade(w, r)
		if err != nil {
			fmt.Println(err)
			return
		}

		stream, err := wtSession.AcceptStream(ctx)
		if err != nil {
			fmt.Println(err)
			return
		}

		bytes, err := io.ReadAll(stream)
		if err != nil {
			fmt.Println(err)
			return
		}

		fmt.Println(string(bytes))

		stream.Write(bytes)

		err = stream.Close()
		if err != nil {
			fmt.Println(err)
			return
		}
	})

	log.Fatal(wtServer.ListenAndServe())
}

func runClient() {
	ctx := context.Background()

	var d webtransport.Dialer
	_, wtSession, err := d.Dial(ctx, "https://example.com", nil)
	if err != nil {
		panic(err)
	}

	stream, err := wtSession.OpenStreamSync(ctx)
	if err != nil {
		panic(err)
	}

	stream.Write([]byte("Hi there"))
	stream.CancelWrite(0)

	bytes, err := io.ReadAll(stream)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(bytes))
}

func getTlsConfig() *tls.Config {
	certmagic.DefaultACME.DisableHTTPChallenge = true
	certmagic.DefaultACME.Agreed = true

	certmagic.Default.OnDemand = &certmagic.OnDemandConfig{
		DecisionFunc: func(ctx context.Context, name string) error {
			return nil
		},
	}
	certConfig := certmagic.NewDefault()

	tlsConfig := &tls.Config{
		GetCertificate: certConfig.GetCertificate,
		NextProtos:     []string{"h3", "http/1.1", "acme-tls/1"},
	}

	return tlsConfig
}

I'm never seeing anything printed, either on the client or server side. Interestingly, if I sleep for a second before stream.CancelWrite(0) then I get an error log: "stream canceled with error code 0". I think I'm incorrectly assuming that EOF will be sent after a call to CancelWrite. Is that not the case? If not, is there a correct way to signal end of stream from the writer side?

@anderspitman
Copy link
Author

I believe I've solved the problem. My misunderstanding is that I was assuming CancelWrite() was the correct way to close the send side of a stream. But according to the go-quic docs here, calling Close is the way to do it. I had assumed that would also close the receive side, but that's not the case. After sawpping CancelWrite for Close my code appears to be working.

@anderspitman
Copy link
Author

@marten-seemann would you be interested in a PR that adds an examples directory and a simple echo server example, or is that not something you want in the repo?

Looks like I'm not the first to struggle with this: #103

@marten-seemann
Copy link
Member

Sure, sounds like a good idea! Want to create a PR?

@anderspitman
Copy link
Author

Submitted #122

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

2 participants