• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package nghttp2
2
3import (
4	"bufio"
5	"bytes"
6	"cmp"
7	"context"
8	"crypto/tls"
9	"encoding/binary"
10	"errors"
11	"fmt"
12	"io"
13	"net"
14	"net/http"
15	"net/http/httptest"
16	"net/url"
17	"os"
18	"os/exec"
19	"slices"
20	"strconv"
21	"strings"
22	"syscall"
23	"testing"
24	"time"
25
26	"github.com/tatsuhiro-t/go-nghttp2"
27	"golang.org/x/net/http2"
28	"golang.org/x/net/http2/hpack"
29	"golang.org/x/net/websocket"
30)
31
32const (
33	serverBin  = buildDir + "/src/nghttpx"
34	serverPort = 3009
35	testDir    = sourceDir + "/integration-tests"
36	logDir     = buildDir + "/integration-tests"
37)
38
39func pair(name, value string) hpack.HeaderField {
40	return hpack.HeaderField{
41		Name:  name,
42		Value: value,
43	}
44}
45
46type serverTester struct {
47	cmd           *exec.Cmd // test frontend server process, which is test subject
48	url           string    // test frontend server URL
49	t             *testing.T
50	ts            *httptest.Server // backend server
51	frontendHost  string           // frontend server host
52	backendHost   string           // backend server host
53	conn          net.Conn         // connection to frontend server
54	h2PrefaceSent bool             // HTTP/2 preface was sent in conn
55	nextStreamID  uint32           // next stream ID
56	fr            *http2.Framer    // HTTP/2 framer
57	headerBlkBuf  bytes.Buffer     // buffer to store encoded header block
58	enc           *hpack.Encoder   // HTTP/2 HPACK encoder
59	header        http.Header      // received header fields
60	dec           *hpack.Decoder   // HTTP/2 HPACK decoder
61	authority     string           // server's host:port
62	frCh          chan http2.Frame // used for incoming HTTP/2 frame
63	errCh         chan error
64}
65
66type options struct {
67	// args is the additional arguments to nghttpx.
68	args []string
69	// handler is the handler to handle the request.  It defaults
70	// to noopHandler.
71	handler http.HandlerFunc
72	// connectPort is the server side port where client connection
73	// is made.  It defaults to serverPort.
74	connectPort int
75	// tls, if set to true, sets up TLS frontend connection.
76	tls bool
77	// tlsConfig is the client side TLS configuration that is used
78	// when tls is true.
79	tlsConfig *tls.Config
80	// tcpData is additional data that are written to connection
81	// before TLS handshake starts.  This field is ignored if tls
82	// is false.
83	tcpData []byte
84	// quic, if set to true, sets up QUIC frontend connection.
85	// quic implies tls = true.
86	quic bool
87}
88
89// newServerTester creates test context.
90func newServerTester(t *testing.T, opts options) *serverTester {
91	if opts.quic {
92		opts.tls = true
93	}
94
95	if opts.handler == nil {
96		opts.handler = noopHandler
97	}
98
99	if opts.connectPort == 0 {
100		opts.connectPort = serverPort
101	}
102
103	ts := httptest.NewUnstartedServer(opts.handler)
104
105	var (
106		args                                                                                        []string
107		backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS, affinityCookie, alpnH1 bool
108	)
109
110	for _, k := range opts.args {
111		switch k {
112		case "--http2-bridge":
113			backendTLS = true
114		case "--dns":
115			dns = true
116		case "--external-dns":
117			dns = true
118			externalDNS = true
119		case "--accept-proxy-protocol":
120			acceptProxyProtocol = true
121		case "--redirect-if-not-tls":
122			redirectIfNotTLS = true
123		case "--affinity-cookie":
124			affinityCookie = true
125		case "--alpn-h1":
126			alpnH1 = true
127		default:
128			args = append(args, k)
129		}
130	}
131
132	if backendTLS {
133		nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{})
134		// According to httptest/server.go, we have to set
135		// NextProtos separately for ts.TLS.  NextProtos set
136		// in nghttp2.ConfigureServer is effectively ignored.
137		ts.TLS = new(tls.Config)
138		ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2")
139		ts.StartTLS()
140
141		args = append(args, "-k")
142	} else {
143		ts.Start()
144	}
145
146	scheme := "http"
147
148	if opts.tls {
149		scheme = "https"
150
151		args = append(args, testDir+"/server.key", testDir+"/server.crt")
152	}
153
154	backendURL, err := url.Parse(ts.URL)
155	if err != nil {
156		t.Fatalf("Error parsing URL from httptest.Server: %v", err)
157	}
158
159	// URL.Host looks like "127.0.0.1:8080", but we want
160	// "127.0.0.1,8080"
161	b := "-b"
162
163	if !externalDNS {
164		b += fmt.Sprintf("%v;", strings.Replace(backendURL.Host, ":", ",", -1))
165	} else {
166		sep := strings.LastIndex(backendURL.Host, ":")
167		if sep == -1 {
168			t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host)
169		}
170
171		// We use awesome service nip.io.
172		b += fmt.Sprintf("%v.nip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
173	}
174
175	if backendTLS {
176		b += ";proto=h2;tls"
177	}
178
179	if dns {
180		b += ";dns"
181	}
182
183	if redirectIfNotTLS {
184		b += ";redirect-if-not-tls"
185	}
186
187	if affinityCookie {
188		b += ";affinity=cookie;affinity-cookie-name=affinity;affinity-cookie-path=/foo/bar"
189	}
190
191	noTLS := ";no-tls"
192
193	if opts.tls {
194		noTLS = ""
195	}
196
197	var proxyProto string
198
199	if acceptProxyProtocol {
200		proxyProto = ";proxyproto"
201	}
202
203	args = append(args, fmt.Sprintf("-f127.0.0.1,%v%v%v", serverPort, noTLS, proxyProto), b,
204		"--errorlog-file="+logDir+"/log.txt", "-LINFO")
205
206	if opts.quic {
207		args = append(args,
208			fmt.Sprintf("-f127.0.0.1,%v;quic", serverPort),
209			"--no-quic-bpf")
210	}
211
212	authority := fmt.Sprintf("127.0.0.1:%v", opts.connectPort)
213
214	st := &serverTester{
215		cmd:          exec.Command(serverBin, args...),
216		t:            t,
217		ts:           ts,
218		url:          fmt.Sprintf("%v://%v", scheme, authority),
219		frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort),
220		backendHost:  backendURL.Host,
221		nextStreamID: 1,
222		authority:    authority,
223		frCh:         make(chan http2.Frame),
224		errCh:        make(chan error),
225	}
226
227	st.cmd.Stdout = os.Stdout
228	st.cmd.Stderr = os.Stderr
229
230	if err := st.cmd.Start(); err != nil {
231		st.t.Fatalf("Error starting %v: %v", serverBin, err)
232	}
233
234	retry := 0
235
236	for {
237		time.Sleep(50 * time.Millisecond)
238
239		conn, err := net.Dial("tcp", authority)
240		if err == nil && opts.tls {
241			if len(opts.tcpData) > 0 {
242				if _, err := conn.Write(opts.tcpData); err != nil {
243					st.Close()
244					st.t.Fatal("Error writing TCP data")
245				}
246			}
247
248			var tlsConfig *tls.Config
249
250			if opts.tlsConfig == nil {
251				tlsConfig = new(tls.Config)
252			} else {
253				tlsConfig = opts.tlsConfig.Clone()
254			}
255
256			tlsConfig.InsecureSkipVerify = true
257
258			if alpnH1 {
259				tlsConfig.NextProtos = []string{"http/1.1"}
260			} else {
261				tlsConfig.NextProtos = []string{"h2"}
262			}
263
264			tlsConn := tls.Client(conn, tlsConfig)
265
266			err = tlsConn.Handshake()
267			if err == nil {
268				conn = tlsConn
269			}
270		}
271
272		if err != nil {
273			retry++
274			if retry >= 100 {
275				st.Close()
276				st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid")
277			}
278
279			continue
280		}
281
282		st.conn = conn
283
284		break
285	}
286
287	st.fr = http2.NewFramer(st.conn, st.conn)
288	st.enc = hpack.NewEncoder(&st.headerBlkBuf)
289	st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) {
290		st.header.Add(f.Name, f.Value)
291	})
292
293	return st
294}
295
296func (st *serverTester) Close() {
297	if st.conn != nil {
298		st.conn.Close()
299	}
300
301	if st.cmd != nil {
302		done := make(chan struct{})
303
304		go func() {
305			if err := st.cmd.Wait(); err != nil {
306				st.t.Errorf("Error st.cmd.Wait() = %v", err)
307			}
308
309			close(done)
310		}()
311
312		if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil {
313			st.t.Errorf("Error st.cmd.Process.Signal() = %v", err)
314		}
315
316		select {
317		case <-done:
318		case <-time.After(10 * time.Second):
319			if err := st.cmd.Process.Kill(); err != nil {
320				st.t.Errorf("Error st.cmd.Process.Kill() = %v", err)
321			}
322
323			<-done
324		}
325	}
326
327	if st.ts != nil {
328		st.ts.Close()
329	}
330}
331
332func (st *serverTester) readFrame() (http2.Frame, error) {
333	go func() {
334		f, err := st.fr.ReadFrame()
335		if err != nil {
336			st.errCh <- err
337			return
338		}
339
340		st.frCh <- f
341	}()
342
343	select {
344	case f := <-st.frCh:
345		return f, nil
346	case err := <-st.errCh:
347		return nil, err
348	case <-time.After(5 * time.Second):
349		return nil, errors.New("timeout waiting for frame")
350	}
351}
352
353type requestParam struct {
354	name        string              // name for this request to identify the request in log easily
355	streamID    uint32              // stream ID, automatically assigned if 0
356	method      string              // method, defaults to GET
357	scheme      string              // scheme, defaults to http
358	authority   string              // authority, defaults to backend server address
359	path        string              // path, defaults to /
360	header      []hpack.HeaderField // additional request header fields
361	body        []byte              // request body
362	trailer     []hpack.HeaderField // trailer part
363	httpUpgrade bool                // true if upgraded to HTTP/2 through HTTP Upgrade
364	noEndStream bool                // true if END_STREAM should not be sent
365}
366
367// wrapper for request body to set trailer part
368type chunkedBodyReader struct {
369	trailer        []hpack.HeaderField
370	trailerWritten bool
371	body           io.Reader
372	req            *http.Request
373}
374
375func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
376	// document says that we have to set http.Request.Trailer
377	// after request was sent and before body returns EOF.
378	if !cbr.trailerWritten {
379		cbr.trailerWritten = true
380
381		for _, h := range cbr.trailer {
382			cbr.req.Trailer.Set(h.Name, h.Value)
383		}
384	}
385
386	return cbr.body.Read(p)
387}
388
389func (st *serverTester) websocket(rp requestParam) *serverResponse {
390	urlstring := st.url + "/echo"
391
392	config, err := websocket.NewConfig(urlstring, st.url)
393	if err != nil {
394		st.t.Fatalf("websocket.NewConfig(%q, %q) returned error: %v", urlstring, st.url, err)
395	}
396
397	config.Header.Add("Test-Case", rp.name)
398
399	for _, h := range rp.header {
400		config.Header.Add(h.Name, h.Value)
401	}
402
403	ws, err := websocket.NewClient(config, st.conn)
404	if err != nil {
405		st.t.Fatalf("Error creating websocket client: %v", err)
406	}
407
408	if _, err := ws.Write(rp.body); err != nil {
409		st.t.Fatalf("ws.Write() returned error: %v", err)
410	}
411
412	msg := make([]byte, 1024)
413
414	var n int
415
416	if n, err = ws.Read(msg); err != nil {
417		st.t.Fatalf("ws.Read() returned error: %v", err)
418	}
419
420	res := &serverResponse{
421		body: msg[:n],
422	}
423
424	return res
425}
426
427func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
428	method := "GET"
429
430	if rp.method != "" {
431		method = rp.method
432	}
433
434	var (
435		body io.Reader
436		cbr  *chunkedBodyReader
437	)
438
439	if rp.body != nil {
440		body = bytes.NewBuffer(rp.body)
441
442		if len(rp.trailer) != 0 {
443			cbr = &chunkedBodyReader{
444				trailer: rp.trailer,
445				body:    body,
446			}
447
448			body = cbr
449		}
450	}
451
452	reqURL := st.url
453
454	if rp.path != "" {
455		u, err := url.Parse(st.url)
456		if err != nil {
457			st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
458		}
459
460		u.Path = ""
461		u.RawQuery = ""
462		reqURL = u.String() + rp.path
463	}
464
465	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
466	defer cancel()
467
468	req, err := http.NewRequestWithContext(ctx, method, reqURL, body)
469	if err != nil {
470		return nil, err
471	}
472
473	for _, h := range rp.header {
474		req.Header.Add(h.Name, h.Value)
475	}
476
477	req.Header.Add("Test-Case", rp.name)
478
479	if cbr != nil {
480		cbr.req = req
481		// this makes request use chunked encoding
482		req.ContentLength = -1
483		req.Trailer = make(http.Header)
484
485		for _, h := range cbr.trailer {
486			req.Trailer.Set(h.Name, "")
487		}
488	}
489
490	if err := req.Write(st.conn); err != nil {
491		return nil, err
492	}
493
494	resp, err := http.ReadResponse(bufio.NewReader(st.conn), req)
495	if err != nil {
496		return nil, err
497	}
498
499	respBody, err := io.ReadAll(resp.Body)
500	if err != nil {
501		return nil, err
502	}
503
504	resp.Body.Close()
505
506	res := &serverResponse{
507		status:    resp.StatusCode,
508		header:    resp.Header,
509		body:      respBody,
510		connClose: resp.Close,
511	}
512
513	return res, nil
514}
515
516func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
517	st.headerBlkBuf.Reset()
518	st.header = make(http.Header)
519
520	var id uint32
521
522	if rp.streamID != 0 {
523		id = rp.streamID
524		if id >= st.nextStreamID && id%2 == 1 {
525			st.nextStreamID = id + 2
526		}
527	} else {
528		id = st.nextStreamID
529		st.nextStreamID += 2
530	}
531
532	if !st.h2PrefaceSent {
533		st.h2PrefaceSent = true
534		fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
535
536		if err := st.fr.WriteSettings(); err != nil {
537			return nil, err
538		}
539	}
540
541	res := &serverResponse{
542		streamID: id,
543	}
544
545	streams := make(map[uint32]*serverResponse)
546	streams[id] = res
547
548	if !rp.httpUpgrade {
549		method := "GET"
550		if rp.method != "" {
551			method = rp.method
552		}
553
554		_ = st.enc.WriteField(pair(":method", method))
555
556		scheme := "http"
557
558		if rp.scheme != "" {
559			scheme = rp.scheme
560		}
561
562		_ = st.enc.WriteField(pair(":scheme", scheme))
563
564		authority := st.authority
565
566		if rp.authority != "" {
567			authority = rp.authority
568		}
569
570		_ = st.enc.WriteField(pair(":authority", authority))
571
572		path := "/"
573
574		if rp.path != "" {
575			path = rp.path
576		}
577
578		_ = st.enc.WriteField(pair(":path", path))
579		_ = st.enc.WriteField(pair("test-case", rp.name))
580
581		for _, h := range rp.header {
582			_ = st.enc.WriteField(h)
583		}
584
585		err := st.fr.WriteHeaders(http2.HeadersFrameParam{
586			StreamID:      id,
587			EndStream:     len(rp.body) == 0 && len(rp.trailer) == 0 && !rp.noEndStream,
588			EndHeaders:    true,
589			BlockFragment: st.headerBlkBuf.Bytes(),
590		})
591		if err != nil {
592			return nil, err
593		}
594
595		if len(rp.body) != 0 {
596			// TODO we assume rp.body fits in 1 frame
597			if err := st.fr.WriteData(id, len(rp.trailer) == 0 && !rp.noEndStream, rp.body); err != nil {
598				return nil, err
599			}
600		}
601
602		if len(rp.trailer) != 0 {
603			st.headerBlkBuf.Reset()
604
605			for _, h := range rp.trailer {
606				_ = st.enc.WriteField(h)
607			}
608
609			err := st.fr.WriteHeaders(http2.HeadersFrameParam{
610				StreamID:      id,
611				EndStream:     true,
612				EndHeaders:    true,
613				BlockFragment: st.headerBlkBuf.Bytes(),
614			})
615			if err != nil {
616				return nil, err
617			}
618		}
619	}
620loop:
621	for {
622		fr, err := st.readFrame()
623		if err != nil {
624			return res, err
625		}
626
627		switch f := fr.(type) {
628		case *http2.HeadersFrame:
629			_, err := st.dec.Write(f.HeaderBlockFragment())
630			if err != nil {
631				return res, err
632			}
633
634			sr, ok := streams[f.FrameHeader.StreamID]
635			if !ok {
636				st.header = make(http.Header)
637				break
638			}
639
640			sr.header = cloneHeader(st.header)
641
642			var status int
643
644			status, err = strconv.Atoi(sr.header.Get(":status"))
645			if err != nil {
646				return res, fmt.Errorf("Error parsing status code: %w", err)
647			}
648
649			sr.status = status
650
651			if f.StreamEnded() && streamEnded(res, streams, sr) {
652				break loop
653			}
654		case *http2.PushPromiseFrame:
655			_, err := st.dec.Write(f.HeaderBlockFragment())
656			if err != nil {
657				return res, err
658			}
659
660			sr := &serverResponse{
661				streamID:  f.PromiseID,
662				reqHeader: cloneHeader(st.header),
663			}
664
665			streams[sr.streamID] = sr
666		case *http2.DataFrame:
667			sr, ok := streams[f.FrameHeader.StreamID]
668			if !ok {
669				break
670			}
671
672			sr.body = append(sr.body, f.Data()...)
673
674			if f.StreamEnded() && streamEnded(res, streams, sr) {
675				break loop
676			}
677		case *http2.RSTStreamFrame:
678			sr, ok := streams[f.FrameHeader.StreamID]
679			if !ok {
680				break
681			}
682
683			sr.errCode = f.ErrCode
684
685			if streamEnded(res, streams, sr) {
686				break loop
687			}
688		case *http2.GoAwayFrame:
689			if f.ErrCode == http2.ErrCodeNo {
690				break
691			}
692
693			res.errCode = f.ErrCode
694			res.connErr = true
695
696			break loop
697		case *http2.SettingsFrame:
698			if f.IsAck() {
699				break
700			}
701
702			if err := st.fr.WriteSettingsAck(); err != nil {
703				return res, err
704			}
705		}
706	}
707
708	slices.SortFunc(res.pushResponse, func(a, b *serverResponse) int {
709		return cmp.Compare(a.streamID, b.streamID)
710	})
711
712	return res, nil
713}
714
715func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool {
716	delete(streams, sr.streamID)
717
718	if mainSr.streamID != sr.streamID {
719		mainSr.pushResponse = append(mainSr.pushResponse, sr)
720	}
721
722	return len(streams) == 0
723}
724
725type serverResponse struct {
726	status       int               // HTTP status code
727	header       http.Header       // response header fields
728	body         []byte            // response body
729	streamID     uint32            // stream ID in HTTP/2
730	errCode      http2.ErrCode     // error code received in HTTP/2 RST_STREAM or GOAWAY
731	connErr      bool              // true if HTTP/2 connection error
732	connClose    bool              // Connection: close is included in response header in HTTP/1 test
733	reqHeader    http.Header       // http request header, currently only stores pushed request header
734	pushResponse []*serverResponse // pushed response
735}
736
737func cloneHeader(h http.Header) http.Header {
738	h2 := make(http.Header, len(h))
739
740	for k, vv := range h {
741		vv2 := make([]string, len(vv))
742		copy(vv2, vv)
743		h2[k] = vv2
744	}
745
746	return h2
747}
748
749func noopHandler(w http.ResponseWriter, r *http.Request) {
750	if _, err := io.ReadAll(r.Body); err != nil {
751		http.Error(w, fmt.Sprintf("Error io.ReadAll() = %v", err), http.StatusInternalServerError)
752	}
753}
754
755type APIResponse struct {
756	Status string                 `json:"status,omitempty"`
757	Code   int                    `json:"code,omitempty"`
758	Data   map[string]interface{} `json:"data,omitempty"`
759}
760
761type proxyProtocolV2 struct {
762	command            proxyProtocolV2Command
763	sourceAddress      net.Addr
764	destinationAddress net.Addr
765	additionalData     []byte
766}
767
768type proxyProtocolV2Command int
769
770const (
771	proxyProtocolV2CommandLocal proxyProtocolV2Command = 0x0
772	proxyProtocolV2CommandProxy proxyProtocolV2Command = 0x1
773)
774
775type proxyProtocolV2Family int
776
777const (
778	proxyProtocolV2FamilyUnspec proxyProtocolV2Family = 0x0
779	proxyProtocolV2FamilyInet   proxyProtocolV2Family = 0x1
780	proxyProtocolV2FamilyInet6  proxyProtocolV2Family = 0x2
781	proxyProtocolV2FamilyUnix   proxyProtocolV2Family = 0x3
782)
783
784type proxyProtocolV2Protocol int
785
786const (
787	proxyProtocolV2ProtocolUnspec proxyProtocolV2Protocol = 0x0
788	proxyProtocolV2ProtocolStream proxyProtocolV2Protocol = 0x1
789	proxyProtocolV2ProtocolDgram  proxyProtocolV2Protocol = 0x2
790)
791
792func writeProxyProtocolV2(w io.Writer, hdr proxyProtocolV2) error {
793	if _, err := w.Write([]byte{0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A}); err != nil {
794		return err
795	}
796
797	if _, err := w.Write([]byte{byte(0x20 | hdr.command)}); err != nil {
798		return err
799	}
800
801	switch srcAddr := hdr.sourceAddress.(type) {
802	case *net.TCPAddr:
803		dstAddr := hdr.destinationAddress.(*net.TCPAddr)
804
805		if len(srcAddr.IP) != len(dstAddr.IP) {
806			panic("len(srcAddr.IP) != len(dstAddr.IP)")
807		}
808
809		var fam byte
810
811		if len(srcAddr.IP) == 4 {
812			fam = byte(proxyProtocolV2FamilyInet << 4)
813		} else {
814			fam = byte(proxyProtocolV2FamilyInet6 << 4)
815		}
816
817		fam |= byte(proxyProtocolV2ProtocolStream)
818
819		if _, err := w.Write([]byte{fam}); err != nil {
820			return err
821		}
822
823		length := uint16(len(srcAddr.IP)*2 + 4 + len(hdr.additionalData))
824
825		if err := binary.Write(w, binary.BigEndian, length); err != nil {
826			return err
827		}
828
829		if _, err := w.Write(srcAddr.IP); err != nil {
830			return err
831		}
832
833		if _, err := w.Write(dstAddr.IP); err != nil {
834			return err
835		}
836
837		if err := binary.Write(w, binary.BigEndian, uint16(srcAddr.Port)); err != nil {
838			return err
839		}
840
841		if err := binary.Write(w, binary.BigEndian, uint16(dstAddr.Port)); err != nil {
842			return err
843		}
844	case *net.UnixAddr:
845		dstAddr := hdr.destinationAddress.(*net.UnixAddr)
846
847		if len(srcAddr.Name) > 108 {
848			panic("too long Unix source address")
849		}
850
851		if len(dstAddr.Name) > 108 {
852			panic("too long Unix destination address")
853		}
854
855		fam := byte(proxyProtocolV2FamilyUnix << 4)
856
857		switch srcAddr.Net {
858		case "unix":
859			fam |= byte(proxyProtocolV2ProtocolStream)
860		case "unixdgram":
861			fam |= byte(proxyProtocolV2ProtocolDgram)
862		default:
863			fam |= byte(proxyProtocolV2ProtocolUnspec)
864		}
865
866		if _, err := w.Write([]byte{fam}); err != nil {
867			return err
868		}
869
870		length := uint16(216 + len(hdr.additionalData))
871
872		if err := binary.Write(w, binary.BigEndian, length); err != nil {
873			return err
874		}
875
876		zeros := make([]byte, 108)
877
878		if _, err := w.Write([]byte(srcAddr.Name)); err != nil {
879			return err
880		}
881
882		if _, err := w.Write(zeros[:108-len(srcAddr.Name)]); err != nil {
883			return err
884		}
885
886		if _, err := w.Write([]byte(dstAddr.Name)); err != nil {
887			return err
888		}
889
890		if _, err := w.Write(zeros[:108-len(dstAddr.Name)]); err != nil {
891			return err
892		}
893	default:
894		fam := byte(proxyProtocolV2FamilyUnspec<<4) | byte(proxyProtocolV2ProtocolUnspec)
895
896		if _, err := w.Write([]byte{fam}); err != nil {
897			return err
898		}
899
900		length := uint16(len(hdr.additionalData))
901
902		if err := binary.Write(w, binary.BigEndian, length); err != nil {
903			return err
904		}
905	}
906
907	if _, err := w.Write(hdr.additionalData); err != nil {
908		return err
909	}
910
911	return nil
912}
913