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