Code Monkey home page Code Monkey logo

webtransport-go's Introduction

A QUIC implementation in pure Go

Documentation PkgGoDev Code Coverage Fuzzing Status

quic-go is an implementation of the QUIC protocol (RFC 9000, RFC 9001, RFC 9002) in Go. It has support for HTTP/3 (RFC 9114), including QPACK (RFC 9204) and HTTP Datagrams (RFC 9297).

In addition to these base RFCs, it also implements the following RFCs:

Support for WebTransport over HTTP/3 (draft-ietf-webtrans-http3) is implemented in webtransport-go.

Detailed documentation can be found on quic-go.net.

Projects using quic-go

Project Description Stars
AdGuardHome Free and open source, powerful network-wide ads & trackers blocking DNS server. GitHub Repo stars
algernon Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support GitHub Repo stars
caddy Fast, multi-platform web server with automatic HTTPS GitHub Repo stars
cloudflared A tunneling daemon that proxies traffic from the Cloudflare network to your origins GitHub Repo stars
frp A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet GitHub Repo stars
go-libp2p libp2p implementation in Go, powering Kubo (IPFS) and Lotus (Filecoin), among others GitHub Repo stars
gost A simple security tunnel written in Go GitHub Repo stars
Hysteria A powerful, lightning fast and censorship resistant proxy GitHub Repo stars
Mercure An open, easy, fast, reliable and battery-efficient solution for real-time communications GitHub Repo stars
OONI Probe Next generation OONI Probe. Library and CLI tool. GitHub Repo stars
RoadRunner High-performance PHP application server, process manager written in Go and powered with plugins GitHub Repo stars
syncthing Open Source Continuous File Synchronization GitHub Repo stars
traefik The Cloud Native Application Proxy GitHub Repo stars
v2ray-core A platform for building proxies to bypass network restrictions GitHub Repo stars
YoMo Streaming Serverless Framework for Geo-distributed System GitHub Repo stars

If you'd like to see your project added to this list, please send us a PR.

Release Policy

quic-go always aims to support the latest two Go releases.

Contributing

We are always happy to welcome new contributors! We have a number of self-contained issues that are suitable for first-time contributors, they are tagged with help wanted. If you have any questions, please feel free to reach out by opening an issue or leaving a comment.

webtransport-go's People

Contributors

dependabot[bot] avatar galargh avatar hareku avatar lleyton avatar marcopolo avatar marten-seemann avatar sukunrt avatar web3-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webtransport-go's Issues

Corruption of unknown origin in data sent through unidirectional stream

I unfortunately don't know much about this issue but I have thoroughly documented the events and my setup here: https://stackoverflow.com/questions/76177807/corruption-of-unknown-origin-in-audio-libopus-and-video-nvenc-hevc-bitstream/76190994?noredirect=1#comment134363768_76190994

Specifically, I sent pure hevc and opus bitstreams through unidirectional streams, but they're getting corrupted some way as depicted in the video (https://youtu.be/wAY5w4zlku4). I know the issue is at the web transport stream since I switched to using datagrams and the corruption is gone.

flaky TestStreamsImmediateReset

=== RUN   TestStreamsImmediateReset
    webtransport_test.go:257: 
        	Error Trace:	webtransport_test.go:257
        	Error:      	Received unexpected error:
        	            	Application error 0x105: received HTTP/3 frame on bidirectional stream
        	Test:       	TestStreamsImmediateReset
--- FAIL: TestStreamsImmediateReset (0.01s)

Goroutine Leak monitoring request

I am experimenting with the codebase and I noticed goroutine leak regarding a goroutine quic-go http3/client.go monitoring the request after it was hijacked.

//`http3/client.go
func (c *client) RoundTrip(req *http.Request) (*http.Response, error) {
...
	// Request Cancellation:
	// This go routine keeps running even after RoundTrip() returns.
	// It is shut down when the application is done processing the body.
	reqDone := make(chan struct{})
	go func() {
		select {
		case <-req.Context().Done():
			str.CancelWrite(quic.StreamErrorCode(errorRequestCanceled))
			str.CancelRead(quic.StreamErrorCode(errorRequestCanceled))
		case <-reqDone:
		}
	}()
...
}

I got around this by making the context sent to Dial canceled-able and cleaning it up.

Help with echo server

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?

Readable streams never end

Here is a repro: https://github.com/achingbrain/go-webtransport-readable-never-ends largely based on interop/main.go from this repo.

The details are in the readme, but running npm start starts a webtransport-go server that listens on a port, accepts incoming WebTransport sessions, waits for an incoming bidirectional stream to be opened and pipes the stream back to itself.

Some JavaScript is printed out that you can paste into https://codepen.io/pen/?editors=0012 and see it run.

It connects to the webtransport-go server, opens a bidirectional stream, writes a single message into the stream and closes the writable, then reads from the readable until it closes.

The output is:

"CLIENT create session"
"CLIENT wait for session"
"CLIENT session ready"
"CLIENT create bidi stream"
"CLIENT get writer"
"CLIENT wait for writer"
"CLIENT write"
"CLIENT close writer"
"CLIENT read from stream"
"CLIENT got from stream" // [object Object] 
{
  "done": false,
  "value": {
    "0": 0,
    "1": 1,
    "2": 2,
    "3": 3
  }
}
"CLIENT read from stream"

After the final "CLIENT read from stream", the readable is read from again and it should return { done: true } to signal it's ending but no further data is received.

The server end seems to see the stream finish so perhaps the FIN isn't being sent?

2024/03/10 15:02:13 SERVER incoming session
2024/03/10 15:02:13 SERVER wait for stream
2024/03/10 15:02:13 SERVER echo stream back to itself
2024/03/10 15:02:13 SERVER stream finished

flaky TestOpenStreamSyncShutdown/bidirectional_streams

=== RUN   TestOpenStreamSyncShutdown/bidirectional_streams
    webtransport_test.go:445: 
        	Error Trace:	/home/runner/work/webtransport-go/webtransport-go/webtransport_test.go:433
        	            				/home/runner/work/webtransport-go/webtransport-go/webtransport_test.go:445
        	Error:      	Should be in error chain:
        	            	expected: %!q(**webtransport.ConnectionError=0xc0000102b0)
        	            	in chain: 
        	Test:       	TestOpenStreamSyncShutdown/bidirectional_streams
=== RUN   TestOpenStreamSyncShutdown/unidirectional_streams
--- FAIL: TestOpenStreamSyncShutdown (0.23s)
    --- FAIL: TestOpenStreamSyncShutdown/bidirectional_streams (0.12s)
    --- PASS: TestOpenStreamSyncShutdown/unidirectional_streams (0.12s)

Support a transport listener interface

It would be interesting for this module to just return the listener and its connection to the application and let the application to have the fun, can we?

type interface TransportListener
{
  func Listen() (net.Listener, error)
}

Media streaming problem

In the uniStream, io.ReadAll for receiving each video frames always causes "deadline exceeded" error.
How can i receive the frames simply without collecting partial packets?
This is the case that client and server are coded in golang.

Large or Many Writes on a Stream get blocked

I am facing an issue where, when I am sending too much data at once over a BidirectionalStream, it gets blocked on .Write() and this part of the application just hangs. Other UnidirectionalStreams, which send much smaller packets, still work simultaneously even when the first stream is "blocked".

(Update: this may be a Firefox issue, since I cannot reproduce this with a Go client or Chromium ...)

For lack of a better method โ€“ stepping through the application with VSCode produces lots of "timed out" and "stream closed" errors โ€“ I tried inserting some fmt.Println() messages and I think I can pinpoint it to the <-s.writeChan line in quic-go/send_stream.go. (Maybe this issue would be better suited in that repository but I am experiencing this issue in the context of WebTransport in the Browser, so I am first reporting it here.)

This happens either with a single large (~ 32 MB) write or multiple smaller (~ 100 KB) writes in short succession. If I insert a 10 ms delay between those smaller packets, everything runs smoothly. But that can't be the correct way to go about this ...


I tried to write a minimal reproducer here: https://gist.github.com/ansemjo/72e1fc95a8c6e19cabfa750e45eae185

Running the WebTransport server with Go go1.20.5 linux/amd64 and opening the page in Firefox 115.0.1 (64-bit), I get:

$ go run main.go
2023/07/11 16:38:05 WebTransport listening on https://localhost:7443/transport (UDP)
2023/07/11 16:38:05 static files listening on https://localhost:7443/
2023/07/11 16:38:07 connection from 127.0.0.1:45628
2023/07/11 16:38:07 write blob #00
2023/07/11 16:38:07 write blob #01
2023/07/11 16:38:07 write blob #02
2023/07/11 16:38:07 write blob #03
2023/07/11 16:38:07 write blob #04
2023/07/11 16:38:07 write blob #05
2023/07/11 16:38:07 write blob #06
2023/07/11 16:38:07 write blob #07
2023/07/11 16:38:07 write blob #08
2023/07/11 16:38:07 write blob #09
2023/07/11 16:38:07 write blob #10
2023/07/11 16:38:07 write blob #11
2023/07/11 16:38:07 write blob #12
2023/07/11 16:38:07 write blob #13
2023/07/11 16:38:07 write blob #14
2023/07/11 16:38:07 write blob #15
2023/07/11 16:38:07 write blob #16
2023/07/11 16:38:07 write blob #17

image

Notably, the server never reaches the log.Println("done sending.") line and Firefox always receives data in 64 KiB chunks ... If I uncomment the time.Sleep(10 * time.Millisecond) in the loop, everything is sent correctly. In my application, the stream is passed on to a Worker and sending blocks even earlier.

Is this a race condition? Is there a way to "flush the buffer" or anything like that?


UPDATE: unfortunately (or luckily, however you look at it ..) I can neither reproduce this with a Go client, nor Chromium ...

How to negotiate a subprotocol ?

WebTransport is raw as it provides an ordered stream of bytes. To make it useful for my case I need to negotiate a subprotocol, so that my client and server know what serializer to use to "parse" the length-prefixed messaging that I am going to do.

Would it make sense to use a Sec-WebTransport-Protocol header before calling Upgrade ? i.e. something like

w.Header().Add("Sec-WebTransport-Protocol", "wamp.2.cbor")

Or is there a more elegant way ?

Firefox cannot dial webtransport-go server

serverCertificateHashes landed in Firefox nightly recently, hooray! But the browser still doesn't seem to be able to dial webtransport-go.

Here's a simple echo server (skip the Node.js bit of the README and just look at the Browsers section).

The go code is largely taken from here.

$ git clone https://github.com/achingbrain/webtransport-dial-go-server.git
$ cd webtransport-dial-go-server
$ npm i
$ npm run browser

> [email protected] browser
> node browser.js

Paste the following code into https://codepen.io or similar:

(async function main ()  {
  console.info('CLIENT create session')
// ...more code here

Running the code in Chrome almost* yields the expected result:

"CLIENT create session"
"CLIENT wait for session"
"CLIENT session ready"
"CLIENT create bidi stream"
"CLIENT get writer"
"CLIENT wait for writer"
"CLIENT write"
"CLIENT close writer"
"CLIENT read from stream"
"CLIENT got from stream" // [object Object] 
{
  "done": false,
  "value": {
    "0": 0,
    "1": 1,
    "2": 2,
    "3": 3
  }
}
"CLIENT read from stream"

When running in Firefox Nightly (125.0a1 (2024-03-05) (64-bit)), the session never becomes ready:

"CLIENT create session"
"CLIENT wait for session"

* = no CLIENT read stream finished message is printed in Chrome so the read stream does not end

can not create lot of connection at the same time

I have tried to test some basic performance of this package. The test code is in this repo webtransport benchmark.
when the client create conn about 100 -200 ๏ผŒthe quic client can not create connection.

QuicConfig: &quic.Config{
				HandshakeIdleTimeout: time.Minute,
				MaxIdleTimeout:       time.Hour,
				MaxIncomingStreams:   1 << 20,
				Allow0RTT:            true,
			},

I have checkouted server config and client config๏ผŒand adjust config params, but there is no effective. Is there some suggestion about this?bench-server

WebTransport Stream Reset Error on Chrome Browser Reload/Disconnects

Description
I am facing an issue with a webtransport-go server during a WebTransport stream session from the Chrome browser. The error appears when the browser is reloaded. Additionally, I would like guidance on handling browser disconnects effectively.

Steps to Reproduce
Set up a WebTransport stream session from Chrome browser to a webtransport-go server.
Reload the Chrome browser.
Expected Behavior
The WebTransport stream should continue without errors or handle disconnects gracefully.

Actual Behavior
When the Chrome browser is reloaded, the following error occurs:

ERRO[0045] stream reset, but failed to convert stream error 386759528: error code outside of expected range

Query Regarding Browser Disconnects
In addition to the above issue, I would like to ask for best practices or recommended methods for handling browser disconnects in webtransport-go. How should the server be configured to handle unexpected browser terminations or reloads to ensure stability and continuity of the stream session?

webtransport.Stream implementing net.Conn

Hi,

Context:

Thank you for a great library. I have started implementing a layer on top of webtransport-go (when HTTP/3 connectivity is available on the network) with a fallback to yamux over HTTP/1.1 upgraded connections (when HTTP/3 fails to connect).

Proposal:

yamux multiplexed streams implement net.Conn. webtransport.Stream almost implements net.Conn - it is missing LocalAddr() net.Addr and RemoteAddr() net.Addr. These could be implemented by just returning the values of the underlying webtransport.Session. This would be valuable to me as then I can have a wrapper that returns net.Conn in either scenario.

Do you see any reason why this would be undesirable? Was not implementing these methods intentional, or just a use case that had yet to be considered?

how to use webtransport client without tls

I have tried some way to run webtransport client without tls., but I got this error:

dial err: CRYPTO_ERROR 0x170 (remote): tls: unrecognized name

the client code like this:

func RunClient() {
	var d = webtransport.Dialer{
		RoundTripper: &http3.RoundTripper{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}
	ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5)
	defer cancel()
	rsp, conn, err := d.Dial(ctx, "https://127.0.0.1:80", nil)
	// overUDP
	if err != nil {
		log.Fatalln("dial err:", err)
	}
	fmt.Println("conn:", conn)
	fmt.Println("rsp:", rsp)
	stream, err := conn.OpenStream()
	if err != nil {
		log.Fatalln("err:", err)
		return
	}
	go func() {
		maxData := make([]byte, 4096)
		for {
			recvMsg, err := stream.Read(maxData)
			if err != nil {
				_ = stream.Close()
				return
			}
			fmt.Println("recvMsg:", recvMsg)
		}
	}()
	write, err := stream.Write([]byte("hello"))
	if err != nil {
		fmt.Println("send err:", write)
		return
	}
	fmt.Println("send msg:", write)
}

the dial url can not use http scheme insteandof https

flaky TestOpenStreamSyncShutdown

https://github.com/quic-go/webtransport-go/actions/runs/4070366077/jobs/7011132327

=== RUN   TestOpenStreamSyncShutdown/bidirectional_streams
    webtransport_test.go:449: 
        	Error Trace:	/home/runner/work/webtransport-go/webtransport-go/webtransport_test.go:437
        	            				/home/runner/work/webtransport-go/webtransport-go/webtransport_test.go:449
        	Error:      	Should be in error chain:
        	            	expected: %!q(**webtransport.ConnectionError=0x9412f98)
        	            	in chain: 
        	Test:       	TestOpenStreamSyncShutdown/bidirectional_streams

Closing a bidirectional stream

I am implementing a server and client to stream video using webtransport. I am using TypeScript on the client, which opens two bidirectional stream (one for audio and one for video) and sends a single request to the server. The server then proceeds to send all file chunks (video is segmented like a DASH video) as fast as possible.

My main issue currently is that when I don't close the stream and try to stream more than one video, eventually the server crashes either through receiving a Unidirectional Stream (which I don't initiate) or by throwing the error "no recent network activity".
So far I have tried to close the StreamWriter on the client using writer.close(), similarily on the server by closing the stream s.Close() and also doing both.

However, from what I can see the streams don't actually close and eventually the server crashes.

Also a minor side question, is there a simple example what a server should look like using webtransport-go? Like an echo server for example. I know there is am exaple on the basic setup on the server in the README but there is no code on how to handle the connection.

Unable to connect with self-signed certificate

I'm trying to set up a client and server locally on my machine using a self-signed certificate generated with openssl. The server setup is copied from the readme, and the client initiation is rsp, conn, err := d.Dial(context.Background(), "https://localhost:443/webtransport", nil). The error I'm getting is "CRYPTO_ERROR 0x12a (local): tls: failed to verify certificate: x509: certificate signed by unknown authority" on the Dial call. Is there some other configuration I'm missing?

flaky TestBidirectionalStreamsDataTransfer/server-initiated test

=== RUN   TestBidirectionalStreamsDataTransfer/server-initiated
    webtransport_test.go:88: 
        	Error Trace:	/Users/runner/work/webtransport-go/webtransport-go/webtransport_test.go:88
        	            				/Users/runner/work/webtransport-go/webtransport-go/webtransport_test.go:158
        	Error:      	Received unexpected error:
        	            	timeout: no recent network activity
        	Test:       	TestBidirectionalStreamsDataTransfer/server-initiated
--- FAIL: TestBidirectionalStreamsDataTransfer (5.06s)
    --- PASS: TestBidirectionalStreamsDataTransfer/client-initiated (0.01s)
    --- FAIL: TestBidirectionalStreamsDataTransfer/server-initiated (5.06s)

Reporting a vulnerability

Hello!

I hope you are doing well!

We are a security research team. Our tool automatically detected a vulnerability in this repository. We want to disclose it responsibly. GitHub has a feature called Private vulnerability reporting, which enables security research to privately disclose a vulnerability. Unfortunately, it is not enabled for this repository.

Can you enable it, so that we can report it?

Thanks in advance!

PS: you can read about how to enable private vulnerability reporting here: https://docs.github.com/en/code-security/security-advisories/repository-security-advisories/configuring-private-vulnerability-reporting-for-a-repository

messages sent from server to browser are fragmented somewhere

I noticed that when sending messages via streams from a Golang server to a browser client, sometimes the message arrives whole, while other times the browser receives a collection of smaller messages instead. In these cases, the smaller messages combined seem to form the message I intended to send.

What's going on here? is this behavior intended? and if so, can we have it so that the browser withholds the messages until it fully arrives.

give a echo example in repo

I have tried some way to run a webtransport example.,but it can not run correctly, the code is like this:


package main

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

	"github.com/quic-go/quic-go"
	"github.com/quic-go/quic-go/http3"
	"github.com/quic-go/webtransport-go"

	"transport-go/testdata"
)

func RunServer() {
	// create a new webtransport.Server, listening on (UDP) port 443
	s := webtransport.Server{
		H3: http3.Server{
			QuicConfig: &quic.Config{Allow0RTT: true},
			// TLSConfig:  &tls.Config{InsecureSkipVerify: true}
		},
	}

	// Create a new HTTP endpoint /webtransport.
	http.HandleFunc("/webtransport", func(w http.ResponseWriter, r *http.Request) {
		conn, err := s.Upgrade(w, r)
		if err != nil {
			log.Printf("upgrading failed: %s", err)
			w.WriteHeader(500)
			return
		}
		fmt.Println("conn:", conn)
		// Handle the connection. Here goes the application logic.
		go func() {
			for {
				stream, err := conn.AcceptStream(context.Background())
				if err != nil {
					fmt.Println("server read err:", err)
					return
				}

				if _, err := io.Copy(stream, stream); err != nil {
					fmt.Println("server write err:", err)
				}
			}

		}()
	})

	if err := s.ListenAndServeTLS(testdata.GetCertificatePaths()); err != nil {
		log.Fatalln("listen err:", err)
	}
}

func RunClient() {

	pool, err := x509.SystemCertPool()
	if err != nil {
		log.Fatal(err)
	}
	testdata.AddRootCA(pool)

	var d = webtransport.Dialer{
		RoundTripper: &http3.RoundTripper{
			TLSClientConfig: &tls.Config{
				RootCAs:            pool,
				InsecureSkipVerify: true,
			},
		},
	}
	// ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5)
	// defer cancel()
	rsp, conn, err := d.Dial(context.Background(), "https://localhost:443", nil)
	// overUDP
	if err != nil {
		log.Fatalln("dial err:", err)
	}
	fmt.Println("conn:", conn)
	fmt.Println("rsp:", rsp)
	stream, err := conn.OpenStream()
	if err != nil {
		log.Fatalln("err:", err)
		return
	}
	go func() {
		maxData := make([]byte, 4096)
		for {
			recvMsg, err := stream.Read(maxData)
			if err != nil {
				_ = stream.Close()
				return
			}
			fmt.Println("recvMsg:", recvMsg)
		}
	}()
	write, err := stream.Write([]byte("hello"))
	if err != nil {
		fmt.Println("send err:", write)
		return
	}
	fmt.Println("send msg:", write)
}

func main() {
	go RunServer()
	time.Sleep(time.Second * 2)
	go RunClient()
	time.Sleep(time.Second * 10)
}

tls config is coped from quic-go internal testdata. The error is like this:

2023/10/31 09:47:41 dial err: received status 404

Maybe something went wrong, I can't debug the code. Could the code repository contain complete examples

Typescript bindings for webtransport

I realize this is all about golang, but connecting from the browser in typescript projects would be nice. Anybody got ideas? Next time I'll post questions like this in discussions, once they are enabled.

TLS issue for ECDSA cert

net::ERR_QUIC_PROTOCOL_ERROR.QUIC_TLS_CERTIFICATE_UNKNOWN (TLS handshake failure (ENCRYPTION_HANDSHAKE) 46: certificate unknown).

Server Freezes for Duration of MaxIdleTimeout on Closing Multiple Unidirectional Streams

Title:
Server Freezes for Duration of MaxIdleTimeout on Closing Multiple Unidirectional Streams

Issue Body:

Description:
I'm using a unidirectional stream where one client is the primary streamer (trough Ingest channel) and multiple clients receive messages from this stream (trough Delivery channel). When refreshing the browser for one of the clients, there's no noticeable impact on the system. However, when I close one more connection, the server seems to freeze for the duration specified in MaxIdleTimeout.

Steps to Reproduce:

Start a primary client streaming on a unidirectional stream.
Connect multiple clients to receive messages from this stream.
Refresh the browser for one of the clients - observe that there's no impact.
Close the connection for one of the clients.
Observe that the server freezes and remains unresponsive for the duration of MaxIdleTimeout.
Expected Behavior:
Closing or refreshing a client's connection should not cause the server to freeze or become unresponsive, regardless of the MaxIdleTimeout setting.

Actual Behavior:
The server becomes unresponsive and only restores after the MaxIdleTimeout duration when multiple client connections are closed. Additionally, it seems that during this MaxIdleTimeout, the server is attempting to stream data to the closed connection without success.

Environment:

Go version: [e.g., 1.17]
OS: [e.g., macOS Big Sur]
Any other relevant environment details.
Additional Context:
The stream is set up such that one client primarily streams data, and multiple clients receive these messages. It's essential for our use case that the server remains stable and responsive even when multiple clients disconnect.

quic-go: HasPacingBudget Issue

Facing this issue with quic-go. Where my binary crashes on low network connectivity

github.com/quic-go/quic-go/internal/congestion.(*cubicSender).HasPacingBudget

goroutine leak in runSendQueue

go.uber.org/goleak detected a goroutine leak. I'd appreciate it if you could investigate this issue.

Code: aptpod@fd28449

Result:

--- FAIL: TestUnidirectionalStreams (0.70s)
    d:\Code\github\webtransport-go\webtransport_test.go:328: found unexpected goroutines:
        [Goroutine 77 in state IO wait, with internal/poll.runtime_pollWait on top of the stack:
        goroutine 77 [IO wait]:
        internal/poll.runtime_pollWait(0x222ec317ee8, 0x72)
        	C:/Program Files/Go/src/runtime/netpoll.go:343 +0x85
        internal/poll.(*pollDesc).wait(0xc000404798?, 0xc000507b38?, 0x0)
        	C:/Program Files/Go/src/internal/poll/fd_poll_runtime.go:84 +0x27
        internal/poll.execIO(0xc000404798, 0x10c1118)
        	C:/Program Files/Go/src/internal/poll/fd_windows.go:175 +0xe6
        internal/poll.(*FD).ReadFromInet6(0xc000404780, {0xc0001eb800, 0xc000121b00?, 0xc000507c20?}, 0xc000507c78)
        	C:/Program Files/Go/src/internal/poll/fd_windows.go:637 +0x145
        net.(*netFD).readFromInet6(0xc000404780, {0xc0001eb800?, 0xc0001209c0?, 0xc000507c60?}, 0xc971ba?)
        	C:/Program Files/Go/src/net/fd_posix.go:72 +0x25
        net.(*UDPConn).readFrom(0x30?, {0xc0001eb800?, 0xc0002868d0?, 0x0?}, 0xc0002868d0)
        	C:/Program Files/Go/src/net/udpsock_posix.go:59 +0x79
        net.(*UDPConn).readFromUDP(0xc0000980b8, {0xc0001eb800?, 0x4?, 0x1046ec0?}, 0x4?)
        	C:/Program Files/Go/src/net/udpsock.go:149 +0x30
        net.(*UDPConn).ReadFrom(0x13930a0?, {0xc0001eb800, 0x5ac, 0x5ac})
        	C:/Program Files/Go/src/net/udpsock.go:158 +0x4a
        github.com/quic-go/quic-go.(*basicConn).ReadPacket(0xc0001180d8)
        	C:/Users/user/go/pkg/mod/github.com/quic-go/[email protected]/sys_conn.go:95 +0xab
        github.com/quic-go/quic-go.(*Transport).listen(0xc000416000, {0x11246a8, 0xc0001180d8})
        	C:/Users/user/go/pkg/mod/github.com/quic-go/[email protected]/transport.go:338 +0x172
        created by github.com/quic-go/quic-go.(*Transport).init.func1 in goroutine 6
        	C:/Users/user/go/pkg/mod/github.com/quic-go/[email protected]/transport.go:242 +0x450
        
         Goroutine 78 in state select, with github.com/quic-go/quic-go.(*Transport).runSendQueue on top of the stack:
        goroutine 78 [select]:
        github.com/quic-go/quic-go.(*Transport).runSendQueue(0xc000416000)
        	C:/Users/user/go/pkg/mod/github.com/quic-go/[email protected]/transport.go:267 +0x107
        created by github.com/quic-go/quic-go.(*Transport).init.func1 in goroutine 6
        	C:/Users/user/go/pkg/mod/github.com/quic-go/[email protected]/transport.go:243 +0x48c
        ]
FAIL
FAIL	github.com/quic-go/webtransport-go	0.997s
FAIL

Memory Leaks in session manager

Just build and run the interop/main.go, then open browser and refresh, the m.conns will always increase and will not be deleted after the WebTransport disconnected.

Error on server upgrade: "webtransport: request origin not allowed"

Issue Description

Attempting to connect via WebTransport from browser to the server results in a failure with the error message "webtransport: request origin not allowed". This issue occurs when handling requests on the server.

Error Message

webtransport: request origin not allowed

Steps to Reproduce

  1. Configure the server with HTTP/3 support and TLS.
  2. Handle incoming requests and attempt to upgrade to a WebTransport session.
  3. The upgrade fails and logs the above error message.

Expected Behavior

The server should successfully upgrade to a WebTransport session without any errors.

Actual Behavior

The server upgrade process fails, and the error "webtransport: request origin not allowed" is logged.

Environment

  • Go version: 1.20 and 1.18
  • OS: Mac M1 Max

Code Snippet

func main() {
	server := webtransport.Server{
		H3: http3.Server{
			Addr:       ":8444",
			QuicConfig: &quic.Config{Allow0RTT: true},
			TLSConfig:  &tls.Config{InsecureSkipVerify: true},
		},
	}

	log.SetFormatter(&log.TextFormatter{})

	http.HandleFunc("/moqingest/", func(rw http.ResponseWriter, r *http.Request) {
		session, err := server.Upgrade(rw, r)
		if err != nil {
			log.Printf("upgrading failed: %s", err)
			rw.WriteHeader(500)
			return
		}

		ingestSessionID := "I-" + uuid.New().String() + "-" + r.URL.Path

		log.Info(fmt.Sprintf("%s - Accepted incoming WebTransport session. rawQuery: %s", ingestSessionID, r.URL.RawQuery))

		handleWebTransportIngestStreams(session, ingestSessionID)
	})

	http.HandleFunc("/moqdelivery/", func(rw http.ResponseWriter, r *http.Request) {
		session, err := server.Upgrade(rw, r)
		if err != nil {
			log.Printf("upgrading failed: %s", err)
			rw.WriteHeader(500)
			return
		}

		deliverySessionID := "D-" + uuid.New().String() + "-" + r.URL.Path

		log.Info(fmt.Sprintf("%s - Accepted incoming WebTransport session. rawQuery: %s", deliverySessionID, r.URL.RawQuery))
		handleWebTransportDeliveryStreams(session, deliverySessionID)
	})

	if err := server.ListenAndServeTLS("../certs/certificate.pem", "../certs/certificate.key"); err != nil {
		log.Fatalln("listen err:", err)
	}
}

Panic on assignment to entry in nil map at `streamsMap.AddStream`

On new session handleConn eventually closes the session executed in a goroutine. As part of this closure, streamsMap is set to nil.

I wonder if it is possible for session manager to return a session that is already closed, which could then result in a panic below.

panic: assignment to entry in nil map
goroutine 2004647120 [running]:
github.com/quic-go/webtransport-go.(*streamsMap).AddStream(0xc194fdbde8, 0xc15f2954a0?, 0xc025dc8b70)
	/go/pkg/mod/github.com/quic-go/[email protected]/streams_map.go:24 +0x66
github.com/quic-go/webtransport-go.(*Session).addStream(0xc194fdbd10, {0x7f2b66fb6cf0?, 0xc15f2954a0}, 0x60?)
	/go/pkg/mod/github.com/quic-go/[email protected]/session.go:173 +0x152
github.com/quic-go/webtransport-go.(*Session).OpenStreamSync(0xc194fdbd10, {0x136cb60?, 0xc087ad9920?})
	/go/pkg/mod/github.com/quic-go/[email protected]/session.go:324 +0x21e
github.com/libp2p/go-libp2p/p2p/transport/webtransport.(*conn).OpenStream(0x4b23f034feb1?, {0x136cb60?, 0xc087ad9920?})
	/go/pkg/mod/github.com/libp2p/[email protected]/p2p/transport/webtransport/conn.go:48 +0x2b
github.com/libp2p/go-libp2p/p2p/net/swarm.(*Conn).openAndAddStream(0xc0d09e8ab0, {0x136cb60?, 0xc087ad9920?}, {0x1376500, 0x1d05f40})
	/go/pkg/mod/github.com/libp2p/[email protected]/p2p/net/swarm/swarm_conn.go:220 +0x42
github.com/libp2p/go-libp2p/p2p/net/swarm.(*Conn).NewStream(0xc0d09e8ab0, {0x136cb60, 0xc087ad9920})
	/go/pkg/mod/github.com/libp2p/[email protected]/p2p/net/swarm/swarm_conn.go:211 +0x175
github.com/libp2p/go-libp2p/p2p/net/swarm.(*Swarm).NewStream(0xc000726000, {0x136cb60, 0xc087ad9920}, {0xc2252abad0, 0x26})
	/go/pkg/mod/github.com/libp2p/[email protected]/p2p/net/swarm/swarm.go:401 +0x1f0
github.com/libp2p/go-libp2p/p2p/host/basic.(*BasicHost).NewStream(0xc00027e780, {0x136cb60, 0xc087ad9920}, {0xc2252abad0, 0x26}, {0xc000868070?, 0x1, 0x1})
	/go/pkg/mod/github.com/libp2p/[email protected]/p2p/host/basic/basic_host.go:626 +0x16f
github.com/libp2p/go-libp2p-kad-dht/internal/net.(*peerMessageSender).prep(0xc23c3a3ae0, {0x136cb60?, 0xc087ad9920?})
	/go/pkg/mod/github.com/libp2p/[email protected]/internal/net/message_manager.go:218 +0x6a
github.com/libp2p/go-libp2p-kad-dht/internal/net.(*peerMessageSender).prepOrInvalidate(0xc23c3a3ae0, {0x136cb60, 0xc087ad9920})
	/go/pkg/mod/github.com/libp2p/[email protected]/internal/net/message_manager.go:200 +0x91
github.com/libp2p/go-libp2p-kad-dht/internal/net.(*messageSenderImpl).messageSenderForPeer(0xc000d88e00, {0x136cb60, 0xc087ad9920}, {0xc2252abad0, 0x26})
	/go/pkg/mod/github.com/libp2p/[email protected]/internal/net/message_manager.go:150 +0x233
github.com/libp2p/go-libp2p-kad-dht/internal/net.(*messageSenderImpl).SendRequest(0xc000d88e00, {0x136cab8, 0xc16b6a9b40}, {0xc2252abad0, 0x26}, 0xe50620?)
	/go/pkg/mod/github.com/libp2p/[email protected]/internal/net/message_manager.go:78 +0xd8
github.com/libp2p/go-libp2p-kad-dht/pb.(*ProtocolMessenger).GetClosestPeers(0xc000868130, {0x136cab8, 0xc16b6a9b40}, {0xc2252abad0, 0x26}, {0xc0633607b0?, 0x0?})
	/go/pkg/mod/github.com/libp2p/[email protected]/pb/protocol_messenger.go:108 +0xd4
github.com/libp2p/go-libp2p-kad-dht.(*IpfsDHT).FindPeer.func1({0x136cab8, 0xc16b6a9b40}, {0xc2252abad0, 0x26})
	/go/pkg/mod/github.com/libp2p/[email protected]/routing.go:604 +0xfb
github.com/libp2p/go-libp2p-kad-dht.(*query).queryPeer(0xc1920f7cb0, {0x136cab8, 0xc16b6a9b40}, 0xc02ce7d120?, {0xc2252abad0, 0x26})
	/go/pkg/mod/github.com/libp2p/[email protected]/query.go:408 +0x26e
created by github.com/libp2p/go-libp2p-kad-dht.(*query).spawnQuery
	/go/pkg/mod/github.com/libp2p/[email protected]/query.go:325 +0x44a

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.