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