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