github.com/TugasAkhir-QUIC/quic-go@v0.0.2-0.20240215011318-d20e25a9054c/integrationtests/self/http_test.go (about)

     1  package self_test
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"compress/gzip"
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"net"
    12  	"net/http"
    13  	"os"
    14  	"strconv"
    15  	"time"
    16  
    17  	"golang.org/x/sync/errgroup"
    18  
    19  	"github.com/TugasAkhir-QUIC/quic-go"
    20  	"github.com/TugasAkhir-QUIC/quic-go/http3"
    21  
    22  	. "github.com/onsi/ginkgo/v2"
    23  	. "github.com/onsi/gomega"
    24  	"github.com/onsi/gomega/gbytes"
    25  )
    26  
    27  type neverEnding byte
    28  
    29  func (b neverEnding) Read(p []byte) (n int, err error) {
    30  	for i := range p {
    31  		p[i] = byte(b)
    32  	}
    33  	return len(p), nil
    34  }
    35  
    36  const deadlineDelay = 250 * time.Millisecond
    37  
    38  var _ = Describe("HTTP tests", func() {
    39  	var (
    40  		mux            *http.ServeMux
    41  		client         *http.Client
    42  		rt             *http3.RoundTripper
    43  		server         *http3.Server
    44  		stoppedServing chan struct{}
    45  		port           int
    46  	)
    47  
    48  	BeforeEach(func() {
    49  		mux = http.NewServeMux()
    50  		mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
    51  			defer GinkgoRecover()
    52  			io.WriteString(w, "Hello, World!\n") // don't check the error here. Stream may be reset.
    53  		})
    54  
    55  		mux.HandleFunc("/prdata", func(w http.ResponseWriter, r *http.Request) {
    56  			defer GinkgoRecover()
    57  			sl := r.URL.Query().Get("len")
    58  			if sl != "" {
    59  				var err error
    60  				l, err := strconv.Atoi(sl)
    61  				Expect(err).NotTo(HaveOccurred())
    62  				w.Write(GeneratePRData(l)) // don't check the error here. Stream may be reset.
    63  			} else {
    64  				w.Write(PRData) // don't check the error here. Stream may be reset.
    65  			}
    66  		})
    67  
    68  		mux.HandleFunc("/prdatalong", func(w http.ResponseWriter, r *http.Request) {
    69  			defer GinkgoRecover()
    70  			w.Write(PRDataLong) // don't check the error here. Stream may be reset.
    71  		})
    72  
    73  		mux.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
    74  			defer GinkgoRecover()
    75  			body, err := io.ReadAll(r.Body)
    76  			Expect(err).NotTo(HaveOccurred())
    77  			w.Write(body) // don't check the error here. Stream may be reset.
    78  		})
    79  
    80  		mux.HandleFunc("/remoteAddr", func(w http.ResponseWriter, r *http.Request) {
    81  			defer GinkgoRecover()
    82  			w.Header().Set("X-RemoteAddr", r.RemoteAddr)
    83  			w.WriteHeader(http.StatusOK)
    84  		})
    85  
    86  		server = &http3.Server{
    87  			Handler:    mux,
    88  			TLSConfig:  getTLSConfig(),
    89  			QuicConfig: getQuicConfig(nil),
    90  		}
    91  
    92  		addr, err := net.ResolveUDPAddr("udp", "0.0.0.0:0")
    93  		Expect(err).NotTo(HaveOccurred())
    94  		conn, err := net.ListenUDP("udp", addr)
    95  		Expect(err).NotTo(HaveOccurred())
    96  		port = conn.LocalAddr().(*net.UDPAddr).Port
    97  
    98  		stoppedServing = make(chan struct{})
    99  
   100  		go func() {
   101  			defer GinkgoRecover()
   102  			server.Serve(conn)
   103  			close(stoppedServing)
   104  		}()
   105  	})
   106  
   107  	AfterEach(func() {
   108  		Expect(rt.Close()).NotTo(HaveOccurred())
   109  		Expect(server.Close()).NotTo(HaveOccurred())
   110  		Eventually(stoppedServing).Should(BeClosed())
   111  	})
   112  
   113  	BeforeEach(func() {
   114  		rt = &http3.RoundTripper{
   115  			TLSClientConfig:    getTLSClientConfigWithoutServerName(),
   116  			DisableCompression: true,
   117  			QuicConfig:         getQuicConfig(&quic.Config{MaxIdleTimeout: 10 * time.Second}),
   118  		}
   119  		client = &http.Client{Transport: rt}
   120  	})
   121  
   122  	It("downloads a hello", func() {
   123  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", port))
   124  		Expect(err).ToNot(HaveOccurred())
   125  		Expect(resp.StatusCode).To(Equal(200))
   126  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second))
   127  		Expect(err).ToNot(HaveOccurred())
   128  		Expect(string(body)).To(Equal("Hello, World!\n"))
   129  	})
   130  
   131  	It("sets content-length for small response", func() {
   132  		mux.HandleFunc("/small", func(w http.ResponseWriter, r *http.Request) {
   133  			defer GinkgoRecover()
   134  			w.Write([]byte("foobar"))
   135  		})
   136  
   137  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/small", port))
   138  		Expect(err).ToNot(HaveOccurred())
   139  		Expect(resp.StatusCode).To(Equal(200))
   140  		Expect(resp.Header.Get("Content-Length")).To(Equal(strconv.Itoa(len("foobar"))))
   141  	})
   142  
   143  	It("detects stream errors when server panics when writing response", func() {
   144  		respChan := make(chan struct{})
   145  		mux.HandleFunc("/writing_and_panicking", func(w http.ResponseWriter, r *http.Request) {
   146  			// no recover here as it will interfere with the handler
   147  			w.Write([]byte("foobar"))
   148  			w.(http.Flusher).Flush()
   149  			// wait for the client to receive the response
   150  			<-respChan
   151  			panic(http.ErrAbortHandler)
   152  		})
   153  
   154  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/writing_and_panicking", port))
   155  		close(respChan)
   156  		Expect(err).ToNot(HaveOccurred())
   157  		body, err := io.ReadAll(resp.Body)
   158  		Expect(err).To(HaveOccurred())
   159  		// the body will be a prefix of what's written
   160  		Expect(bytes.HasPrefix([]byte("foobar"), body)).To(BeTrue())
   161  	})
   162  
   163  	It("requests to different servers with the same udpconn", func() {
   164  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/remoteAddr", port))
   165  		Expect(err).ToNot(HaveOccurred())
   166  		Expect(resp.StatusCode).To(Equal(200))
   167  		addr1 := resp.Header.Get("X-RemoteAddr")
   168  		Expect(addr1).ToNot(Equal(""))
   169  		resp, err = client.Get(fmt.Sprintf("https://127.0.0.1:%d/remoteAddr", port))
   170  		Expect(err).ToNot(HaveOccurred())
   171  		Expect(resp.StatusCode).To(Equal(200))
   172  		addr2 := resp.Header.Get("X-RemoteAddr")
   173  		Expect(addr2).ToNot(Equal(""))
   174  		Expect(addr1).To(Equal(addr2))
   175  	})
   176  
   177  	It("downloads concurrently", func() {
   178  		group, ctx := errgroup.WithContext(context.Background())
   179  		for i := 0; i < 2; i++ {
   180  			group.Go(func() error {
   181  				req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/hello", port), nil)
   182  				Expect(err).ToNot(HaveOccurred())
   183  				resp, err := client.Do(req)
   184  				Expect(err).ToNot(HaveOccurred())
   185  				Expect(resp.StatusCode).To(Equal(200))
   186  				body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second))
   187  				Expect(err).ToNot(HaveOccurred())
   188  				Expect(string(body)).To(Equal("Hello, World!\n"))
   189  
   190  				return nil
   191  			})
   192  		}
   193  
   194  		err := group.Wait()
   195  		Expect(err).ToNot(HaveOccurred())
   196  	})
   197  
   198  	It("sets and gets request headers", func() {
   199  		handlerCalled := make(chan struct{})
   200  		mux.HandleFunc("/headers/request", func(w http.ResponseWriter, r *http.Request) {
   201  			defer GinkgoRecover()
   202  			Expect(r.Header.Get("foo")).To(Equal("bar"))
   203  			Expect(r.Header.Get("lorem")).To(Equal("ipsum"))
   204  			close(handlerCalled)
   205  		})
   206  
   207  		req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/headers/request", port), nil)
   208  		Expect(err).ToNot(HaveOccurred())
   209  		req.Header.Set("foo", "bar")
   210  		req.Header.Set("lorem", "ipsum")
   211  		resp, err := client.Do(req)
   212  		Expect(err).ToNot(HaveOccurred())
   213  		Expect(resp.StatusCode).To(Equal(200))
   214  		Eventually(handlerCalled).Should(BeClosed())
   215  	})
   216  
   217  	It("sets and gets response headers", func() {
   218  		mux.HandleFunc("/headers/response", func(w http.ResponseWriter, r *http.Request) {
   219  			defer GinkgoRecover()
   220  			w.Header().Set("foo", "bar")
   221  			w.Header().Set("lorem", "ipsum")
   222  		})
   223  
   224  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/headers/response", port))
   225  		Expect(err).ToNot(HaveOccurred())
   226  		Expect(resp.StatusCode).To(Equal(200))
   227  		Expect(resp.Header.Get("foo")).To(Equal("bar"))
   228  		Expect(resp.Header.Get("lorem")).To(Equal("ipsum"))
   229  	})
   230  
   231  	It("downloads a small file", func() {
   232  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/prdata", port))
   233  		Expect(err).ToNot(HaveOccurred())
   234  		Expect(resp.StatusCode).To(Equal(200))
   235  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 5*time.Second))
   236  		Expect(err).ToNot(HaveOccurred())
   237  		Expect(body).To(Equal(PRData))
   238  	})
   239  
   240  	It("downloads a large file", func() {
   241  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/prdatalong", port))
   242  		Expect(err).ToNot(HaveOccurred())
   243  		Expect(resp.StatusCode).To(Equal(200))
   244  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 20*time.Second))
   245  		Expect(err).ToNot(HaveOccurred())
   246  		Expect(body).To(Equal(PRDataLong))
   247  	})
   248  
   249  	It("downloads many hellos", func() {
   250  		const num = 150
   251  
   252  		for i := 0; i < num; i++ {
   253  			resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", port))
   254  			Expect(err).ToNot(HaveOccurred())
   255  			Expect(resp.StatusCode).To(Equal(200))
   256  			body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second))
   257  			Expect(err).ToNot(HaveOccurred())
   258  			Expect(string(body)).To(Equal("Hello, World!\n"))
   259  		}
   260  	})
   261  
   262  	It("downloads many files, if the response is not read", func() {
   263  		const num = 150
   264  
   265  		for i := 0; i < num; i++ {
   266  			resp, err := client.Get(fmt.Sprintf("https://localhost:%d/prdata", port))
   267  			Expect(err).ToNot(HaveOccurred())
   268  			Expect(resp.StatusCode).To(Equal(200))
   269  			Expect(resp.Body.Close()).To(Succeed())
   270  		}
   271  	})
   272  
   273  	It("posts a small message", func() {
   274  		resp, err := client.Post(
   275  			fmt.Sprintf("https://localhost:%d/echo", port),
   276  			"text/plain",
   277  			bytes.NewReader([]byte("Hello, world!")),
   278  		)
   279  		Expect(err).ToNot(HaveOccurred())
   280  		Expect(resp.StatusCode).To(Equal(200))
   281  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 5*time.Second))
   282  		Expect(err).ToNot(HaveOccurred())
   283  		Expect(body).To(Equal([]byte("Hello, world!")))
   284  	})
   285  
   286  	It("uploads a file", func() {
   287  		resp, err := client.Post(
   288  			fmt.Sprintf("https://localhost:%d/echo", port),
   289  			"text/plain",
   290  			bytes.NewReader(PRData),
   291  		)
   292  		Expect(err).ToNot(HaveOccurred())
   293  		Expect(resp.StatusCode).To(Equal(200))
   294  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 5*time.Second))
   295  		Expect(err).ToNot(HaveOccurred())
   296  		Expect(body).To(Equal(PRData))
   297  	})
   298  
   299  	It("uses gzip compression", func() {
   300  		mux.HandleFunc("/gzipped/hello", func(w http.ResponseWriter, r *http.Request) {
   301  			defer GinkgoRecover()
   302  			Expect(r.Header.Get("Accept-Encoding")).To(Equal("gzip"))
   303  			w.Header().Set("Content-Encoding", "gzip")
   304  			w.Header().Set("foo", "bar")
   305  
   306  			gw := gzip.NewWriter(w)
   307  			defer gw.Close()
   308  			gw.Write([]byte("Hello, World!\n"))
   309  		})
   310  
   311  		client.Transport.(*http3.RoundTripper).DisableCompression = false
   312  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/gzipped/hello", port))
   313  		Expect(err).ToNot(HaveOccurred())
   314  		Expect(resp.StatusCode).To(Equal(200))
   315  		Expect(resp.Uncompressed).To(BeTrue())
   316  
   317  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second))
   318  		Expect(err).ToNot(HaveOccurred())
   319  		Expect(string(body)).To(Equal("Hello, World!\n"))
   320  	})
   321  
   322  	It("handles context cancellations", func() {
   323  		mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) {
   324  			<-r.Context().Done()
   325  		})
   326  
   327  		ctx, cancel := context.WithCancel(context.Background())
   328  		req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil)
   329  		Expect(err).ToNot(HaveOccurred())
   330  		time.AfterFunc(50*time.Millisecond, cancel)
   331  
   332  		_, err = client.Do(req)
   333  		Expect(err).To(HaveOccurred())
   334  		Expect(err).To(MatchError(context.Canceled))
   335  	})
   336  
   337  	It("cancels requests", func() {
   338  		handlerCalled := make(chan struct{})
   339  		mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) {
   340  			defer GinkgoRecover()
   341  			defer close(handlerCalled)
   342  			for {
   343  				if _, err := w.Write([]byte("foobar")); err != nil {
   344  					Expect(r.Context().Done()).To(BeClosed())
   345  					var http3Err *http3.Error
   346  					Expect(errors.As(err, &http3Err)).To(BeTrue())
   347  					Expect(http3Err.ErrorCode).To(Equal(http3.ErrCode(0x10c)))
   348  					Expect(http3Err.Error()).To(Equal("H3_REQUEST_CANCELLED"))
   349  					return
   350  				}
   351  			}
   352  		})
   353  
   354  		req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil)
   355  		Expect(err).ToNot(HaveOccurred())
   356  		ctx, cancel := context.WithCancel(context.Background())
   357  		req = req.WithContext(ctx)
   358  		resp, err := client.Do(req)
   359  		Expect(err).ToNot(HaveOccurred())
   360  		Expect(resp.StatusCode).To(Equal(200))
   361  		cancel()
   362  		Eventually(handlerCalled).Should(BeClosed())
   363  		_, err = resp.Body.Read([]byte{0})
   364  		var http3Err *http3.Error
   365  		Expect(errors.As(err, &http3Err)).To(BeTrue())
   366  		Expect(http3Err.ErrorCode).To(Equal(http3.ErrCode(0x10c)))
   367  		Expect(http3Err.Error()).To(Equal("H3_REQUEST_CANCELLED (local)"))
   368  	})
   369  
   370  	It("allows streamed HTTP requests", func() {
   371  		done := make(chan struct{})
   372  		mux.HandleFunc("/echoline", func(w http.ResponseWriter, r *http.Request) {
   373  			defer GinkgoRecover()
   374  			defer close(done)
   375  			w.WriteHeader(200)
   376  			w.(http.Flusher).Flush()
   377  			reader := bufio.NewReader(r.Body)
   378  			for {
   379  				msg, err := reader.ReadString('\n')
   380  				if err != nil {
   381  					return
   382  				}
   383  				_, err = w.Write([]byte(msg))
   384  				Expect(err).ToNot(HaveOccurred())
   385  				w.(http.Flusher).Flush()
   386  			}
   387  		})
   388  
   389  		r, w := io.Pipe()
   390  		req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("https://localhost:%d/echoline", port), r)
   391  		Expect(err).ToNot(HaveOccurred())
   392  		rsp, err := client.Do(req)
   393  		Expect(err).ToNot(HaveOccurred())
   394  		Expect(rsp.StatusCode).To(Equal(200))
   395  
   396  		reader := bufio.NewReader(rsp.Body)
   397  		for i := 0; i < 5; i++ {
   398  			msg := fmt.Sprintf("Hello world, %d!\n", i)
   399  			fmt.Fprint(w, msg)
   400  			msgRcvd, err := reader.ReadString('\n')
   401  			Expect(err).ToNot(HaveOccurred())
   402  			Expect(msgRcvd).To(Equal(msg))
   403  		}
   404  		Expect(req.Body.Close()).To(Succeed())
   405  		Eventually(done).Should(BeClosed())
   406  	})
   407  
   408  	It("allows taking over the stream", func() {
   409  		mux.HandleFunc("/httpstreamer", func(w http.ResponseWriter, r *http.Request) {
   410  			defer GinkgoRecover()
   411  			w.WriteHeader(200)
   412  			w.(http.Flusher).Flush()
   413  
   414  			str := r.Body.(http3.HTTPStreamer).HTTPStream()
   415  			str.Write([]byte("foobar"))
   416  
   417  			// Do this in a Go routine, so that the handler returns early.
   418  			// This way, we can also check that the HTTP/3 doesn't close the stream.
   419  			go func() {
   420  				defer GinkgoRecover()
   421  				_, err := io.Copy(str, str)
   422  				Expect(err).ToNot(HaveOccurred())
   423  				Expect(str.Close()).To(Succeed())
   424  			}()
   425  		})
   426  
   427  		req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/httpstreamer", port), nil)
   428  		Expect(err).ToNot(HaveOccurred())
   429  		rsp, err := client.Transport.(*http3.RoundTripper).RoundTripOpt(req, http3.RoundTripOpt{DontCloseRequestStream: true})
   430  		Expect(err).ToNot(HaveOccurred())
   431  		Expect(rsp.StatusCode).To(Equal(200))
   432  
   433  		str := rsp.Body.(http3.HTTPStreamer).HTTPStream()
   434  		b := make([]byte, 6)
   435  		_, err = io.ReadFull(str, b)
   436  		Expect(err).ToNot(HaveOccurred())
   437  		Expect(b).To(Equal([]byte("foobar")))
   438  
   439  		data := GeneratePRData(8 * 1024)
   440  		_, err = str.Write(data)
   441  		Expect(err).ToNot(HaveOccurred())
   442  		Expect(str.Close()).To(Succeed())
   443  		repl, err := io.ReadAll(str)
   444  		Expect(err).ToNot(HaveOccurred())
   445  		Expect(repl).To(Equal(data))
   446  	})
   447  
   448  	It("serves other QUIC connections", func() {
   449  		tlsConf := getTLSConfig()
   450  		tlsConf.NextProtos = []string{http3.NextProtoH3}
   451  		ln, err := quic.ListenAddr("localhost:0", tlsConf, nil)
   452  		Expect(err).ToNot(HaveOccurred())
   453  		defer ln.Close()
   454  		done := make(chan struct{})
   455  		go func() {
   456  			defer GinkgoRecover()
   457  			defer close(done)
   458  			conn, err := ln.Accept(context.Background())
   459  			Expect(err).ToNot(HaveOccurred())
   460  			Expect(server.ServeQUICConn(conn)).To(Succeed())
   461  		}()
   462  
   463  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", ln.Addr().(*net.UDPAddr).Port))
   464  		Expect(err).ToNot(HaveOccurred())
   465  		Expect(resp.StatusCode).To(Equal(http.StatusOK))
   466  		client.Transport.(io.Closer).Close()
   467  		Eventually(done).Should(BeClosed())
   468  	})
   469  
   470  	It("supports read deadlines", func() {
   471  		mux.HandleFunc("/read-deadline", func(w http.ResponseWriter, r *http.Request) {
   472  			defer GinkgoRecover()
   473  			rc := http.NewResponseController(w)
   474  			Expect(rc.SetReadDeadline(time.Now().Add(deadlineDelay))).To(Succeed())
   475  
   476  			body, err := io.ReadAll(r.Body)
   477  			Expect(err).To(MatchError(os.ErrDeadlineExceeded))
   478  			Expect(body).To(ContainSubstring("aa"))
   479  
   480  			w.Write([]byte("ok"))
   481  		})
   482  
   483  		expectedEnd := time.Now().Add(deadlineDelay)
   484  		resp, err := client.Post(
   485  			fmt.Sprintf("https://localhost:%d/read-deadline", port),
   486  			"text/plain",
   487  			neverEnding('a'),
   488  		)
   489  		Expect(err).ToNot(HaveOccurred())
   490  		Expect(resp.StatusCode).To(Equal(200))
   491  
   492  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay))
   493  		Expect(err).ToNot(HaveOccurred())
   494  		Expect(time.Now().After(expectedEnd)).To(BeTrue())
   495  		Expect(string(body)).To(Equal("ok"))
   496  	})
   497  
   498  	It("supports write deadlines", func() {
   499  		mux.HandleFunc("/write-deadline", func(w http.ResponseWriter, r *http.Request) {
   500  			defer GinkgoRecover()
   501  			rc := http.NewResponseController(w)
   502  			Expect(rc.SetWriteDeadline(time.Now().Add(deadlineDelay))).To(Succeed())
   503  
   504  			_, err := io.Copy(w, neverEnding('a'))
   505  			Expect(err).To(MatchError(os.ErrDeadlineExceeded))
   506  		})
   507  
   508  		expectedEnd := time.Now().Add(deadlineDelay)
   509  
   510  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/write-deadline", port))
   511  		Expect(err).ToNot(HaveOccurred())
   512  		Expect(resp.StatusCode).To(Equal(200))
   513  
   514  		body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay))
   515  		Expect(err).ToNot(HaveOccurred())
   516  		Expect(time.Now().After(expectedEnd)).To(BeTrue())
   517  		Expect(string(body)).To(ContainSubstring("aa"))
   518  	})
   519  
   520  	It("sets remote address", func() {
   521  		mux.HandleFunc("/remote-addr", func(w http.ResponseWriter, r *http.Request) {
   522  			defer GinkgoRecover()
   523  			_, ok := r.Context().Value(http3.RemoteAddrContextKey).(net.Addr)
   524  			Expect(ok).To(BeTrue())
   525  		})
   526  
   527  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/remote-addr", port))
   528  		Expect(err).ToNot(HaveOccurred())
   529  		Expect(resp.StatusCode).To(Equal(200))
   530  	})
   531  
   532  	It("sets conn context", func() {
   533  		type ctxKey int
   534  		server.ConnContext = func(ctx context.Context, c quic.Connection) context.Context {
   535  			serv, ok := ctx.Value(http3.ServerContextKey).(*http3.Server)
   536  			Expect(ok).To(BeTrue())
   537  			Expect(serv).To(Equal(server))
   538  
   539  			ctx = context.WithValue(ctx, ctxKey(0), "Hello")
   540  			ctx = context.WithValue(ctx, ctxKey(1), c)
   541  			return ctx
   542  		}
   543  		mux.HandleFunc("/conn-context", func(w http.ResponseWriter, r *http.Request) {
   544  			defer GinkgoRecover()
   545  			v, ok := r.Context().Value(ctxKey(0)).(string)
   546  			Expect(ok).To(BeTrue())
   547  			Expect(v).To(Equal("Hello"))
   548  
   549  			c, ok := r.Context().Value(ctxKey(1)).(quic.Connection)
   550  			Expect(ok).To(BeTrue())
   551  			Expect(c).ToNot(BeNil())
   552  
   553  			serv, ok := r.Context().Value(http3.ServerContextKey).(*http3.Server)
   554  			Expect(ok).To(BeTrue())
   555  			Expect(serv).To(Equal(server))
   556  		})
   557  
   558  		resp, err := client.Get(fmt.Sprintf("https://localhost:%d/conn-context", port))
   559  		Expect(err).ToNot(HaveOccurred())
   560  		Expect(resp.StatusCode).To(Equal(200))
   561  	})
   562  })