github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/server/https/https_test.go (about) 1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package https 16 17 import ( 18 "bufio" 19 "bytes" 20 "context" 21 "crypto/ecdsa" 22 "crypto/elliptic" 23 "crypto/rand" 24 "crypto/sha256" 25 "crypto/tls" 26 "crypto/x509" 27 "encoding/base64" 28 "encoding/binary" 29 "encoding/hex" 30 "encoding/pem" 31 "fmt" 32 "io" 33 "math/big" 34 "net" 35 "net/http" 36 "net/url" 37 "strings" 38 "testing" 39 "time" 40 41 log "github.com/golang/glog" 42 "google.golang.org/protobuf/proto" 43 44 "github.com/google/fleetspeak/fleetspeak/src/common" 45 fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 46 "github.com/google/fleetspeak/fleetspeak/src/comtesting" 47 "github.com/google/fleetspeak/fleetspeak/src/server" 48 "github.com/google/fleetspeak/fleetspeak/src/server/comms" 49 "github.com/google/fleetspeak/fleetspeak/src/server/db" 50 "github.com/google/fleetspeak/fleetspeak/src/server/sqlite" 51 "github.com/google/fleetspeak/fleetspeak/src/server/testserver" 52 53 cpb "github.com/google/fleetspeak/fleetspeak/src/server/components/proto/fleetspeak_components" 54 ) 55 56 var ( 57 serverCert []byte 58 ) 59 60 func makeServer(t *testing.T, caseName string, frontendConfig *cpb.FrontendConfig) (*server.Server, *sqlite.Datastore, string) { 61 cert, key, err := comtesting.ServerCert() 62 if err != nil { 63 t.Fatal(err) 64 } 65 serverCert = cert 66 67 addr, err := net.ResolveTCPAddr("tcp", "localhost:0") 68 if err != nil { 69 t.Fatal(err) 70 } 71 tl, err := net.ListenTCP("tcp", addr) 72 if err != nil { 73 t.Fatal(err) 74 } 75 com, err := NewCommunicator(Params{Listener: tl, Cert: cert, Key: key, Streaming: true, FrontendConfig: frontendConfig}) 76 if err != nil { 77 t.Fatal(err) 78 } 79 time.Sleep(time.Second) 80 log.Infof("Communicator listening to: %v", tl.Addr()) 81 ts := testserver.Make(t, "https", caseName, []comms.Communicator{com}) 82 83 return ts.S, ts.DS, tl.Addr().String() 84 } 85 86 func clientCertFingerprint(derBytes []byte) string { 87 // Calculate the SHA-256 digest of the DER certificate 88 sha256Digest := sha256.Sum256(derBytes) 89 90 // Convert the SHA-256 digest to a hexadecimal string 91 sha256HexStr := fmt.Sprintf("%x", sha256Digest) 92 93 sha256Binary, err := hex.DecodeString(sha256HexStr) 94 if err != nil { 95 log.Errorf("error decoding hexdump: %v\n", err) 96 return "" 97 } 98 99 // Convert the hexadecimal string to a base64 encoded string 100 // It also removes trailing "=" padding characters 101 base64EncodedStr := strings.TrimRight(base64.StdEncoding.EncodeToString(sha256Binary), "=") 102 103 // Rreturn the base64 encoded string 104 return base64EncodedStr 105 } 106 107 func makeClient(t *testing.T, doTls bool) (common.ClientID, *http.Client, []byte, string) { 108 // Populate a CertPool with the server's certificate. 109 cp := x509.NewCertPool() 110 if !cp.AppendCertsFromPEM(serverCert) { 111 t.Fatal("Unable to parse server pem.") 112 } 113 114 // Create a key for the client. 115 privKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) 116 if err != nil { 117 t.Fatal(err) 118 } 119 b, err := x509.MarshalECPrivateKey(privKey) 120 if err != nil { 121 t.Fatal(err) 122 } 123 bk := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) 124 125 id, err := common.MakeClientID(privKey.Public()) 126 if err != nil { 127 t.Fatal(err) 128 } 129 130 // Create a self signed cert for client key. 131 tmpl := x509.Certificate{ 132 SerialNumber: big.NewInt(42), 133 } 134 b, err = x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, privKey.Public(), privKey) 135 if err != nil { 136 t.Fatal(err) 137 } 138 fp := clientCertFingerprint(b) 139 bc := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: b}) 140 141 clientCert, err := tls.X509KeyPair(bc, bk) 142 if err != nil { 143 t.Fatal(err) 144 } 145 var cl http.Client 146 if doTls { 147 cl = http.Client{ 148 Transport: &http.Transport{ 149 TLSClientConfig: &tls.Config{ 150 RootCAs: cp, 151 Certificates: []tls.Certificate{clientCert}, 152 }, 153 Dial: (&net.Dialer{ 154 Timeout: 30 * time.Second, 155 KeepAlive: 30 * time.Second, 156 }).Dial, 157 TLSHandshakeTimeout: 10 * time.Second, 158 ExpectContinueTimeout: 1 * time.Second, 159 }, 160 } 161 } else { 162 cl = http.Client{ 163 Transport: &http.Transport{ 164 Dial: (&net.Dialer{ 165 Timeout: 30 * time.Second, 166 KeepAlive: 30 * time.Second, 167 }).Dial, 168 ExpectContinueTimeout: 1 * time.Second, 169 }, 170 } 171 } 172 return id, &cl, bc, fp 173 } 174 175 func TestNormalPoll(t *testing.T) { 176 ctx := context.Background() 177 178 s, ds, addr := makeServer(t, "Normal", nil) 179 id, cl, _, _ := makeClient(t, true) 180 defer s.Stop() 181 182 u := url.URL{Scheme: "https", Host: addr, Path: "/message"} 183 184 // An empty body is a valid, though atypical initial request. 185 resp, err := cl.Post(u.String(), "", nil) 186 if err != nil { 187 t.Fatal(err) 188 } 189 190 b, err := io.ReadAll(resp.Body) 191 if err != nil { 192 t.Error(err) 193 } 194 resp.Body.Close() 195 196 var cd fspb.ContactData 197 if err := proto.Unmarshal(b, &cd); err != nil { 198 t.Errorf("Unable to parse returned data as ContactData: %v", err) 199 } 200 if cd.SequencingNonce == 0 { 201 t.Error("Expected SequencingNonce in returned ContactData") 202 } 203 204 // The client should now exist in the datastore. 205 _, err = ds.GetClientData(ctx, id) 206 if err != nil { 207 t.Errorf("Error getting client data after poll: %v", err) 208 } 209 } 210 211 func TestFile(t *testing.T) { 212 ctx := context.Background() 213 s, ds, addr := makeServer(t, "File", nil) 214 _, cl, _, _ := makeClient(t, true) 215 defer s.Stop() 216 217 data := []byte("The quick sly fox jumped over the lazy dogs.") 218 if err := ds.StoreFile(ctx, "testService", "testFile", bytes.NewReader(data)); err != nil { 219 t.Errorf("Error from StoreFile(testService, testFile): %v", err) 220 } 221 222 u := url.URL{Scheme: "https", Host: addr, Path: "/files/testService/testFile"} 223 resp, err := cl.Get(u.String()) 224 if err != nil { 225 t.Fatal(err) 226 } 227 if resp.StatusCode != http.StatusOK { 228 t.Errorf("Unexpected response code when reading file got [%v] want [%v].", resp.StatusCode, http.StatusOK) 229 } 230 b, err := io.ReadAll(resp.Body) 231 if err != nil { 232 t.Errorf("Unexpected error reading file body: %v", err) 233 } 234 if !bytes.Equal(data, b) { 235 t.Errorf("Unexpected file body, got [%v], want [%v]", string(b), string(data)) 236 } 237 resp.Body.Close() 238 } 239 240 func makeWrapped() []byte { 241 cd := &fspb.ContactData{ 242 ClientClock: db.NowProto(), 243 } 244 b, err := proto.Marshal(cd) 245 if err != nil { 246 log.Fatal(err) 247 } 248 wcd := &fspb.WrappedContactData{ 249 ContactData: b, 250 ClientLabels: []string{"linux", "test"}, 251 } 252 253 buf, err := proto.Marshal(wcd) 254 if err != nil { 255 log.Fatal(err) 256 } 257 sizeBuf := make([]byte, 0, 16) 258 sizeBuf = binary.AppendUvarint(sizeBuf, uint64(len(buf))) 259 260 return append(sizeBuf, buf...) 261 } 262 263 func readContact(body *bufio.Reader) (*fspb.ContactData, error) { 264 size, err := binary.ReadUvarint(body) 265 if err != nil { 266 return nil, err 267 } 268 buf := make([]byte, size) 269 _, err = io.ReadFull(body, buf) 270 if err != nil { 271 return nil, err 272 } 273 var cd fspb.ContactData 274 if err := proto.Unmarshal(buf, &cd); err != nil { 275 return nil, err 276 } 277 return &cd, nil 278 } 279 280 func TestStreaming(t *testing.T) { 281 ctx := context.Background() 282 s, _, addr := makeServer(t, "Streaming", nil) 283 _, cl, _, _ := makeClient(t, true) 284 defer s.Stop() 285 286 br, bw := io.Pipe() 287 go func() { 288 // First exchange - these writes must happen during the http.Client.Do call 289 // below, because the server writes headers at the end of the first message 290 // exchange. 291 292 // Start with the magic number: 293 binary.Write(bw, binary.LittleEndian, magic) 294 295 if _, err := bw.Write(makeWrapped()); err != nil { 296 t.Error(err) 297 } 298 }() 299 300 u := url.URL{Scheme: "https", Host: addr, Path: "/streaming-message"} 301 req, err := http.NewRequest("POST", u.String(), br) 302 req.ContentLength = -1 303 req.Close = true 304 req.Header.Set("Expect", "100-continue") 305 if err != nil { 306 t.Fatal(err) 307 } 308 req = req.WithContext(ctx) 309 resp, err := cl.Do(req) 310 if err != nil { 311 t.Fatalf("Streaming post failed (%v): %v", resp, err) 312 } 313 // Read ContactData for first exchange. 314 body := bufio.NewReader(resp.Body) 315 cd, err := readContact(body) 316 if err != nil { 317 t.Error(err) 318 } 319 if cd.AckIndex != 0 { 320 t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex) 321 } 322 323 for i := uint64(1); i < 10; i++ { 324 // Write another WrappedContactData. 325 if _, err := bw.Write(makeWrapped()); err != nil { 326 t.Error(err) 327 } 328 cd, err := readContact(body) 329 if err != nil { 330 t.Error(err) 331 } 332 if cd.AckIndex != i { 333 t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i) 334 } 335 } 336 337 bw.Close() 338 resp.Body.Close() 339 } 340 341 func TestHeaderNormalPoll(t *testing.T) { 342 ctx := context.Background() 343 clientCertHeader := "ssl-client-cert" 344 frontendConfig := &cpb.FrontendConfig{ 345 FrontendMode: &cpb.FrontendConfig_HttpsHeaderConfig{ 346 HttpsHeaderConfig: &cpb.HttpsHeaderConfig{ 347 ClientCertificateHeader: clientCertHeader, 348 }, 349 }, 350 } 351 352 s, ds, addr := makeServer(t, "Normal", frontendConfig) 353 id, cl, bc, _ := makeClient(t, true) 354 defer s.Stop() 355 356 u := url.URL{Scheme: "https", Host: addr, Path: "/message"} 357 358 req, err := http.NewRequest("POST", u.String(), nil) 359 req.Close = true 360 cc := url.PathEscape(string(bc)) 361 req.Header.Set(clientCertHeader, cc) 362 if err != nil { 363 t.Fatal(err) 364 } 365 366 // An empty body is a valid, though atypical initial request. 367 req = req.WithContext(ctx) 368 resp, err := cl.Do(req) 369 if err != nil { 370 t.Fatal(err) 371 } 372 373 b, err := io.ReadAll(resp.Body) 374 if err != nil { 375 t.Error(err) 376 } 377 resp.Body.Close() 378 379 var cd fspb.ContactData 380 if err := proto.Unmarshal(b, &cd); err != nil { 381 t.Errorf("Unable to parse returned data as ContactData: %v", err) 382 } 383 if cd.SequencingNonce == 0 { 384 t.Error("Expected SequencingNonce in returned ContactData") 385 } 386 387 // The client should now exist in the datastore. 388 _, err = ds.GetClientData(ctx, id) 389 if err != nil { 390 t.Errorf("Error getting client data after poll: %v", err) 391 } 392 } 393 394 func TestHeaderStreaming(t *testing.T) { 395 ctx := context.Background() 396 clientCertHeader := "ssl-client-cert" 397 frontendConfig := &cpb.FrontendConfig{ 398 FrontendMode: &cpb.FrontendConfig_HttpsHeaderConfig{ 399 HttpsHeaderConfig: &cpb.HttpsHeaderConfig{ 400 ClientCertificateHeader: clientCertHeader, 401 }, 402 }, 403 } 404 405 s, _, addr := makeServer(t, "Streaming", frontendConfig) 406 _, cl, bc, _ := makeClient(t, true) 407 defer s.Stop() 408 409 br, bw := io.Pipe() 410 go func() { 411 // First exchange - these writes must happen during the http.Client.Do call 412 // below, because the server writes headers at the end of the first message 413 // exchange. 414 415 // Start with the magic number: 416 binary.Write(bw, binary.LittleEndian, magic) 417 418 if _, err := bw.Write(makeWrapped()); err != nil { 419 t.Error(err) 420 } 421 }() 422 423 u := url.URL{Scheme: "https", Host: addr, Path: "/streaming-message"} 424 req, err := http.NewRequest("POST", u.String(), br) 425 req.ContentLength = -1 426 req.Close = true 427 req.Header.Set("Expect", "100-continue") 428 429 cc := url.PathEscape(string(bc)) 430 req.Header.Set(clientCertHeader, cc) 431 if err != nil { 432 t.Fatal(err) 433 } 434 req = req.WithContext(ctx) 435 resp, err := cl.Do(req) 436 if err != nil { 437 t.Fatalf("Streaming post failed (%v): %v", resp, err) 438 } 439 // Read ContactData for first exchange. 440 body := bufio.NewReader(resp.Body) 441 cd, err := readContact(body) 442 if err != nil { 443 t.Error(err) 444 } 445 if cd.AckIndex != 0 { 446 t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex) 447 } 448 449 for i := uint64(1); i < 10; i++ { 450 // Write another WrappedContactData. 451 if _, err := bw.Write(makeWrapped()); err != nil { 452 t.Error(err) 453 } 454 cd, err := readContact(body) 455 if err != nil { 456 t.Error(err) 457 } 458 if cd.AckIndex != i { 459 t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i) 460 } 461 } 462 463 bw.Close() 464 resp.Body.Close() 465 } 466 467 func TestHeaderStreamingChecksum(t *testing.T) { 468 ctx := context.Background() 469 clientCertHeader := "ssl-client-cert" 470 clientCertChecksumHeader := "ssl-client-cert-checksum" 471 frontendConfig := &cpb.FrontendConfig{ 472 FrontendMode: &cpb.FrontendConfig_HttpsHeaderChecksumConfig{ 473 HttpsHeaderChecksumConfig: &cpb.HttpsHeaderChecksumConfig{ 474 ClientCertificateHeader: clientCertHeader, 475 ClientCertificateChecksumHeader: clientCertChecksumHeader, 476 }, 477 }, 478 } 479 480 s, _, addr := makeServer(t, "Streaming", frontendConfig) 481 _, cl, bc, fp := makeClient(t, true) 482 defer s.Stop() 483 484 br, bw := io.Pipe() 485 go func() { 486 // First exchange - these writes must happen during the http.Client.Do call 487 // below, because the server writes headers at the end of the first message 488 // exchange. 489 490 // Start with the magic number: 491 binary.Write(bw, binary.LittleEndian, magic) 492 493 if _, err := bw.Write(makeWrapped()); err != nil { 494 t.Error(err) 495 } 496 }() 497 498 u := url.URL{Scheme: "https", Host: addr, Path: "/streaming-message"} 499 req, err := http.NewRequest("POST", u.String(), br) 500 if err != nil { 501 t.Fatal(err) 502 } 503 req.ContentLength = -1 504 req.Close = true 505 req.Header.Set("Expect", "100-continue") 506 507 cc := url.PathEscape(string(bc)) 508 req.Header.Set(clientCertHeader, cc) 509 req.Header.Set(clientCertChecksumHeader, fp) 510 req = req.WithContext(ctx) 511 resp, err := cl.Do(req) 512 if err != nil { 513 t.Fatalf("Streaming post failed (%v): %v", resp, err) 514 } 515 // Read ContactData for first exchange. 516 body := bufio.NewReader(resp.Body) 517 cd, err := readContact(body) 518 if cd == nil { 519 t.Fatalf("Read Contact returned nil: %v", err) 520 } 521 if err != nil { 522 t.Error(err) 523 } 524 if cd.AckIndex != 0 { 525 t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex) 526 } 527 528 for i := uint64(1); i < 10; i++ { 529 // Write another WrappedContactData. 530 if _, err := bw.Write(makeWrapped()); err != nil { 531 t.Error(err) 532 } 533 cd, err := readContact(body) 534 if err != nil { 535 t.Error(err) 536 } 537 if cd.AckIndex != i { 538 t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i) 539 } 540 } 541 542 bw.Close() 543 resp.Body.Close() 544 } 545 546 func TestCleartextHeaderStreaming(t *testing.T) { 547 ctx := context.Background() 548 clientCertHeader := "ssl-client-cert" 549 frontendConfig := &cpb.FrontendConfig{ 550 FrontendMode: &cpb.FrontendConfig_CleartextHeaderConfig{ 551 CleartextHeaderConfig: &cpb.CleartextHeaderConfig{ 552 ClientCertificateHeader: clientCertHeader, 553 }, 554 }, 555 } 556 557 s, _, addr := makeServer(t, "Streaming", frontendConfig) 558 _, cl, bc, _ := makeClient(t, false) 559 defer s.Stop() 560 561 br, bw := io.Pipe() 562 go func() { 563 // First exchange - these writes must happen during the http.Client.Do call 564 // below, because the server writes headers at the end of the first message 565 // exchange. 566 567 // Start with the magic number: 568 binary.Write(bw, binary.LittleEndian, magic) 569 570 if _, err := bw.Write(makeWrapped()); err != nil { 571 t.Error(err) 572 } 573 }() 574 575 u := url.URL{Scheme: "http", Host: addr, Path: "/streaming-message"} 576 req, err := http.NewRequest("POST", u.String(), br) 577 req.ContentLength = -1 578 req.Close = true 579 req.Header.Set("Expect", "100-continue") 580 581 cc := url.PathEscape(string(bc)) 582 req.Header.Set(clientCertHeader, cc) 583 if err != nil { 584 t.Fatal(err) 585 } 586 req = req.WithContext(ctx) 587 resp, err := cl.Do(req) 588 if err != nil { 589 t.Fatalf("Streaming post failed (%v): %v", resp, err) 590 } 591 // Read ContactData for first exchange. 592 body := bufio.NewReader(resp.Body) 593 cd, err := readContact(body) 594 if err != nil { 595 t.Error(err) 596 } 597 if cd.AckIndex != 0 { 598 t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex) 599 } 600 601 for i := uint64(1); i < 10; i++ { 602 // Write another WrappedContactData. 603 if _, err := bw.Write(makeWrapped()); err != nil { 604 t.Error(err) 605 } 606 cd, err := readContact(body) 607 if err != nil { 608 t.Error(err) 609 } 610 if cd.AckIndex != i { 611 t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i) 612 } 613 } 614 615 bw.Close() 616 resp.Body.Close() 617 } 618 619 func TestCleartextHeaderStreamingChecksum(t *testing.T) { 620 ctx := context.Background() 621 clientCertHeader := "ssl-client-cert" 622 clientCertChecksumHeader := "ssl-client-cert-checksum" 623 frontendConfig := &cpb.FrontendConfig{ 624 FrontendMode: &cpb.FrontendConfig_CleartextHeaderChecksumConfig{ 625 CleartextHeaderChecksumConfig: &cpb.CleartextHeaderChecksumConfig{ 626 ClientCertificateHeader: clientCertHeader, 627 ClientCertificateChecksumHeader: clientCertChecksumHeader, 628 }, 629 }, 630 } 631 632 s, _, addr := makeServer(t, "Streaming", frontendConfig) 633 _, cl, bc, fp := makeClient(t, false) 634 defer s.Stop() 635 636 br, bw := io.Pipe() 637 go func() { 638 // First exchange - these writes must happen during the http.Client.Do call 639 // below, because the server writes headers at the end of the first message 640 // exchange. 641 642 // Start with the magic number: 643 binary.Write(bw, binary.LittleEndian, magic) 644 645 if _, err := bw.Write(makeWrapped()); err != nil { 646 t.Error(err) 647 } 648 }() 649 650 u := url.URL{Scheme: "http", Host: addr, Path: "/streaming-message"} 651 req, err := http.NewRequest("POST", u.String(), br) 652 if err != nil { 653 t.Fatal(err) 654 } 655 req.ContentLength = -1 656 req.Close = true 657 req.Header.Set("Expect", "100-continue") 658 659 cc := url.PathEscape(string(bc)) 660 req.Header.Set(clientCertHeader, cc) 661 req.Header.Set(clientCertChecksumHeader, fp) 662 req = req.WithContext(ctx) 663 resp, err := cl.Do(req) 664 if err != nil { 665 t.Fatalf("Streaming post failed (%v): %v", resp, err) 666 } 667 // Read ContactData for first exchange. 668 body := bufio.NewReader(resp.Body) 669 cd, err := readContact(body) 670 if cd == nil { 671 t.Fatalf("Read Contact returned nil: %v", err) 672 } 673 if err != nil { 674 t.Error(err) 675 } 676 if cd.AckIndex != 0 { 677 t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex) 678 } 679 680 for i := uint64(1); i < 10; i++ { 681 // Write another WrappedContactData. 682 if _, err := bw.Write(makeWrapped()); err != nil { 683 t.Error(err) 684 } 685 cd, err := readContact(body) 686 if err != nil { 687 t.Error(err) 688 } 689 if cd.AckIndex != i { 690 t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i) 691 } 692 } 693 694 bw.Close() 695 resp.Body.Close() 696 } 697 698 func TestCleartextXfccStreaming(t *testing.T) { 699 ctx := context.Background() 700 clientCertHeader := "ssl-client-cert" 701 frontendConfig := &cpb.FrontendConfig{ 702 FrontendMode: &cpb.FrontendConfig_CleartextXfccConfig{ 703 CleartextXfccConfig: &cpb.CleartextXfccConfig{ 704 ClientCertificateHeader: clientCertHeader, 705 }, 706 }, 707 } 708 709 s, _, addr := makeServer(t, "Streaming", frontendConfig) 710 _, cl, bc, _ := makeClient(t, false) 711 defer s.Stop() 712 713 br, bw := io.Pipe() 714 go func() { 715 // First exchange - these writes must happen during the http.Client.Do call 716 // below, because the server writes headers at the end of the first message 717 // exchange. 718 719 // Start with the magic number: 720 binary.Write(bw, binary.LittleEndian, magic) 721 722 if _, err := bw.Write(makeWrapped()); err != nil { 723 t.Error(err) 724 } 725 }() 726 727 u := url.URL{Scheme: "http", Host: addr, Path: "/streaming-message"} 728 req, err := http.NewRequest("POST", u.String(), br) 729 if err != nil { 730 t.Fatal(err) 731 } 732 req.ContentLength = -1 733 req.Close = true 734 req.Header.Set("Expect", "100-continue") 735 736 cc := url.PathEscape(string(bc)) 737 req.Header.Set(clientCertHeader, `Cert=`+cc) 738 req = req.WithContext(ctx) 739 resp, err := cl.Do(req) 740 if err != nil { 741 t.Fatalf("Streaming post failed (%v): %v", resp, err) 742 } 743 // Read ContactData for first exchange. 744 body := bufio.NewReader(resp.Body) 745 cd, err := readContact(body) 746 if cd == nil { 747 t.Fatalf("Read Contact returned nil: %v", err) 748 } 749 if err != nil { 750 t.Error(err) 751 } 752 if cd.AckIndex != 0 { 753 t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex) 754 } 755 756 for i := uint64(1); i < 10; i++ { 757 // Write another WrappedContactData. 758 if _, err := bw.Write(makeWrapped()); err != nil { 759 t.Error(err) 760 } 761 cd, err := readContact(body) 762 if err != nil { 763 t.Error(err) 764 } 765 if cd.AckIndex != i { 766 t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i) 767 } 768 } 769 770 bw.Close() 771 resp.Body.Close() 772 }